UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) Ingeniero Técnico en Informática de Sistemas PROYECTO FIN DE CARRERA Driver para Control Intuitivo de Ratón por Force Feedback Director: D. Juan Antonio Pérez-Campanero Atanasio Autor: Ernesto Corral Messía de la Cerda Madrid Junio 2010 Autorizada la entrega del proyecto del alumno: Ernesto Corral Messía de la Cerda EL DIRECTOR DEL PROYECTO Juan Antonio Pérez-Campanero Atanasio Fdo.: ........................ Fecha: ....../ ....../ ...... Vº Bº del Coordinador de Proyectos David Contreras Bárcena Fdo.: ........................ Fecha: ....../ ....../ ...... UNIVERSIDAD PONTIFICIA COMILLAS ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA (ICAI) Ingeniero Técnico en Informática de Sistemas PROYECTO FIN DE CARRERA Driver para Control Intuitivo de Ratón por Force Feedback Director: D. Juan Antonio Pérez-Campanero Atanasio Autor: Ernesto Corral Messía de la Cerda Madrid Junio 2010 Este proyecto está protegido mediante una licencia Creative Commons de reconocimiento, sin propósitos comerciales y cuyas obras derivadas cumplan a su vez estos requisitos. http://creativecommons.org/licenses/by-nc-sa/3.0/es/ AGRADECIMIENTOS AGRADECIMIENTOS Quiero dar la gracias en primer lugar a Jaime, diseñador del hardware de este dispositivo, ideólogo de este proyecto y sobre todo, amigo. A Juan Antonio Pérez-Campanero, por dejarme hacer este proyecto con él, y por el tiempo dedicado a este proyecto. A mi madre, por la educación que me ha dado y su apoyo en los peores momentos, tanto en la realización del proyecto como en el resto de la vida. A mi padre, por enseñarme a juguetear con el ordenador y el camino de autoexec.bat para poder hacer funcionar el PcFutbol 3.0 que me llevó hasta aquí. Estés donde estés, si es que estás espero que estés orgulloso. A la Monster School, muchos años juntos y muchas experiencias compartidas, hemos pasado mucho juntos... ¡y lo que nos queda! Empezamos siendo seis y el número ha ido aumentando durante el paso por la universidad. Nos quedan muchas cosas por vivir juntos y espero que sigamos juntos por muchos años. “All your cocos are belong to us”. A Leyre. A Ramón y todos los que compartieron nuestra mesa en el Boomerang. A la gente de San Vicente, Markés, Aratz, Migueloto, Yayo, Markitos, Ch1, Martits, Fabio, Pue, Lud, Carol, Yaiza, Pazos, Epi, Balmori y el resto de la gentuza que nos juntamos allí. A la gente de La Coruña que me acompañado durante tantos veranos y navidades, Alfonso, Asis, Lino, Otero, Alex, y el resto de Coruñeses adictos al Playa que no me llevan al Rush. A Carlos Sierra, gracias por todo. A todos lo compañeros con los que he compartido momentos más allá de las aulas, Harek, Fishfeets (Cifu), Pablo, Raúl, Mario, Weezer, Antonio “Hegemón”, Villa, David, Jorge, Javi, Ignatio, Diego, Luíh. Seguro que me dejo a muchos pero ellos saben quienes son. A los parroquianos del Rey Lagarto, especialmente a las Lagartonnas con las que he compartido grandes momentos. Al hardcore, sin esta música no habría soportado tantas horas de estudio y vii AGRADECIMIENTOS delante del ordenador durante la carrera. Especialmente a la Taking Off Crew y al Straight Edge, sin conocer este último no sé por donde podría haber llegado mi vida, me ayudó mucho a encaminar mi vida. Al resto de mi familia y en general a todo el mundo que ha pasado por mi vida y me ha hecho ser lo que soy. A todos los que me dejo y que deberían estar en esta lista, esto no es más que un papel, donde os llevo de verdad es en el corazón. viii RESUMEN RESUMEN Debido al sistema actual para el manejo de maquinaria industrial y a la clara tendencia al realismo en la industria de los videojuegos se decidió que se desarrollaría un dispositivo que pudiera tanto mejorar el control de la maquinaria industrial, especialmente brazos mecánicos, como mejorar la experiencia de juego en videojuegos, especialmente los clasificados como First Person Shooter. Por ello durante este proyecto se hablará del desarrollo de la parte del software para una primera aproximación a un dispositivo completamente funcional. La parte del hardware fue desarrollada por Jaime González-Arintero durante los años 2008-2009. Se trata de un diseño de lo que sería un prototipo del dispositivo. Con este desarrollo nacería lo que quedó en llamarse Proyecto Rigodon. El dispositivo consiste en la modificación de un ratón simple. A este se le añadirían unos electroimanes que serían controlados por un circuito. Este circuito haría que los electroimanes recibieran una tensión determinada que hiciera que se pegasen a una alfombrilla de hierro sobre la que se apoyaría el ratón dificultando así el movimiento. Con las especificaciones que se daban debido al diseño del hardware se decidió que el proyecto se dividiría en tres grandes módulos. El primer módulo consistió en la necesidad de hacer un driver que facilitase la implementación del ratón en caso de que se llegase a comercializar, de este modo el dispositivo podría ser incluido en programas de una manera más general. Además la realización de este módulo del proyecto ayudaría mucho a la consecución de los objetivos marcados, así como a la realización de los siguientes módulos. A modo de aplicación para realizar pruebas y como forma de utilizar el dispositivo con aplicaciones que no lo tengan implantado, se desarrolló una aplicación para el control de los electroimanes. Esta aplicación tiene un interfaz ix RESUMEN muy simple para que su manejo no necesite de aprendizaje. Por último, como modo de demostrar las posibilidades del dispositivo se adaptó el código fuente de un videojuego de forma que si se intentase vender a una compañía, esta pudiera ver una demostración del dispositivo en acción. Además se realizó un estudio de mercado en el que se llegó a la conclusión de que con una campaña de publicidad adecuada a las necesidades del producto, se podría tener una buena incursión en el mercado, ya que las industrias a las que se pretende llevar este producto, están abiertas a mejoras tecnológicas. x ABSTRACT ABSTRACT Current systems for handle industrial machinery and the tendency to realism in the videogames market led to developing a new device that can make better the control of indrustial machinery, specialy mechanic arms, and improve the experience on videogames, especialy those known as First Person Shooters. This project describes the development of the software for the first approach to a funtional device. Hardware was developed by Jaime González-Arintero during 2008 and 2009. It is the desingn of a prototype of a the device. The project called Rigodon bornt with this development. Device consists of modifying a simple mouse. Some electromagnets will be added the mouse and will be controled by an electronic circuit. The circuit is used to handle the tension that controls force that electromagnets uses to stick the mouse and the metal plank under it, dificulting the movement of the mouse. With conditions gave by the design of the hardware, project needed to be divided in three big modules. First module goes into the need of making a driver that makes easy device implementation in case it will be commercialized, with a driver device could be included in a simple way. The development of this first module will help with the achievement of the project objetives and will help in the development of the next modules. As testing application and as control application for the device in applications that don't have it implanted, an application that control electromagnets was developed. This application has a very simple interface with no learning needed. To use as a demonstration of the posibilities the device, source code of a videogame was adapted to show it to a company in case of trying to sell the device. A market research was executed, the conclusion was that with a good xi ABSTRACT advertising campaign the device could be inserted into the market, due to industries pointed by this proyect are opened to technologic improvements. xii ÍNDICE 1 INTRODUCCIÓN.............................................................6 1.1 Estado del arte....................................................................................8 1.1.1 Tableta gráfica.....................................................................8 1.1.2 Joystick...............................................................................9 1.1.3 Wiimote...............................................................................9 1.1.4 Novint Falcon...................................................................10 1.1.5 Nueva generación de ratones............................................11 1.1.6 Software............................................................................11 1.2 Rigodón.............................................................................................11 1.3 Motivación........................................................................................13 1.4 Objetivos...........................................................................................13 1.5 Requisitos de usuario.......................................................................14 1.6 Requisitos Software..........................................................................14 1.7 Análisis de requisitos........................................................................15 1.8 Metodología......................................................................................16 1.8.1 FASE1: Análisis................................................................17 1.8.2 FASE2: Diseño..................................................................18 1.8.3 FASE3: Implementación...................................................18 1.8.4 FASE4: Pruebas................................................................19 1.9 Planificación.....................................................................................19 2 DISEÑO DEL DRIVER...................................................22 2.1 Diseño de I/O....................................................................................22 2.1.1 Etapa1: Recepción y tratamiento de los datos..................24 2.1.2 Etapa2: Conversión digital-analógica...............................25 2.1.3 Etapa 3: Amplificación/buffer...........................................25 2.1.4 Etapa 4: Salida..................................................................26 2.2 UMDF y KMDF................................................................................26 2.2.1 Infraestructura de UMDF..................................................27 2.3 WinUSB.............................................................................................30 2.3.1 Install.inf...........................................................................31 2.3.2 Funciones en <winusb.h>.................................................31 2.4 Tecnología.........................................................................................32 3 DISEÑO DE LA APLICACIÓN DE CONTROL.........35 3.1 Componentes....................................................................................36 3.1.1 JSlider jFuerza..................................................................36 3.1.2 JTextField txtFuerza..........................................................36 3.2 JNI (Java Native Interface).............................................................36 3.3 Clase DriverCall.java.......................................................................37 3.4 Salida.................................................................................................37 3.5 DriverCall.c......................................................................................38 3.5.1 Java_DriverCall_driverCall..............................................38 3.5.2 GetWinUSBHandle...........................................................39 3.5.3 GetDeviceHandle .............................................................39 3.5.4 QueryDeviceEndpoints ....................................................39 3.5.5 WriteToBulkEndpoint ......................................................40 3.6 Tecnología.........................................................................................40 4 ADAPTACIÓN DE VIDEOJUEGO...............................42 4.1 Elección de videojuego.....................................................................42 4.2 Ingeniería inversa.............................................................................43 4.3 Adaptando el código.........................................................................44 4.4 Tecnología.........................................................................................45 5 CONCLUSIONES Y TRABAJOS FUTUROS..............47 6 ESTUDIO ECONÓMICO...............................................50 6.1 Estudio de mercado..........................................................................50 6.2 Presupuesto.......................................................................................51 7 BIBLIOGRAFÍA..............................................................54 7.1 Libros................................................................................................54 7.2 Links..................................................................................................54 8 ANEXOS...........................................................................56 Anexo A Ejemplo de fichero de instalación .inf...................................56 Anexo B DriverCall.c.............................................................................59 Anexo C Manual de instalación de winUSB........................................73 Anexo D Manual de la aplicación.........................................................74 Anexo E Librería rigodon.h..................................................................75 INTRODUCCIÓN Parte1 INTRODUCCIÓN 5 INTRODUCCIÓN 1 INTRODUCCIÓN En este capítulo se comentará como surgió la idea de realizar este proyecto, también se comentará el estado de la tecnología que afecta al proyecto, así como los motivos que llevaron a su realización, la problemática a la que se hizo frente, una pequeña introducción al dispositivo sobre el que se trabaja, los objetivos a los que se quiere llegar, la metodología utilizada para llegar a ellos y una la planificación realizada para la conclusión de este proyecto. La idea de realizar este proyecto surge a partir de una broma realizada en una comida entre dos amigos en la que se sugirió que sería gracioso que al realizar un movimiento de drag & drop con un fichero que “pesase” poco no hubiera problema, pero que si se tratase de un fichero pesado, de por ejemplo de 2GB, costase realizar el movimiento. Al día siguiente uno de ellos se dio cuenta de que era posible hacerlo mediante un ratón con electroimanes y una alfombrilla de hierro. Tras analizar las aplicaciones que podía tener tanto en la industria como en el mundo de la informática se pusieron manos a la obra. Entre las aplicaciones encontradas para la idea inicial, las más interesantes fueron el manejo de brazos mecánicos en la industria y el proporcionar realismo a una industria que no para de aumentar, la industria del videojuego. Cuando un operario utiliza un brazo mecánico, al operario le da igual lo que transporte ese brazo, ya que el sistema tradicional utiliza un sistema de palancas. La velocidad a la que se mueve ese brazo viene determinada por la tarea para la que ese brazo mecánico está diseñado, por lo que ese brazo siempre se moverá a esa velocidad incluso en el movimiento vacío de carga. Además, ¿qué pasa si se quiere utilizar ese mismo brazo para realizar una tarea distinta? Surgiría un problema ya que para poder cambiar la velocidad a la que se mueve ese brazo, tendría que haber un técnico que lo modificara a nivel de hardware. Las cifras de los últimos años dicen que en la industria audiovisual más del 50% de los ingresos vienen del mercado de los videojuegos. También vemos que la tendencia es a conseguir un mayor realismo en los videojuegos, sobre todo en 6 INTRODUCCIÓN los conocidos como first person shooters, aventuras en primera persona en las que el jugador toma el papel de un mercenario o de un soldado. A la hora de ponerse a jugar, el jugador medio dispone de una pantalla, un teclado y ratón, supóngase que el usuario tiene que hacer de soldado, cada vez que tenga que utilizar un arma, al tener siempre el mismo ratón, la sensación de usar un cuchillo o una pistola va a ser la misma que la de utilizar un arma más grande como un lanza misiles o un bazuca. Figura 1.1: S.T.A.L.K.E.R., ejemplo de First Person Shooter. Con este proyecto se pretende dar los primeros pasos para la creación de un sistema de control que permita una fácil reconfiguración de algunos sistemas en la industria. A la vez se ofrece una opción de un nuevo sistema de control que pretende mejorar la experiencia en los videojuegos. Todo esto orientado a una posible comercialización del producto, por lo tanto cuidando los costes para poder tener un precio competitivo en el mercado. 7 INTRODUCCIÓN 1.1 Estado del arte Actualmente la manera más extendida de controlar un ordenador es mediante la combinación de teclado y ratón, aunque no existen alternativas para el teclado, para controlar el cursor en un entorno gráfico si se presentan varias opciones. Entre las más comunes se encontrarían la tableta gráfica y el joystick, y un poco más infrecuente serían el Wiimote y el Novint Falcon Pistol. 1.1.1 Tableta gráfica Este periférico consiste en una tableta que representa la pantalla, el movimiento del cursor sobre la pantalla se realiza mediante un bolígrafo especial que se desplaza por la pantalla. Este dispositivo está preparado para diseñadores gráficos o profesionales de la edición de imagen, es muy preciso y al manejarse como si fuera un bolígrafo permite hacer trazos que no se pueden realizar con el ratón. El problema de este tipo de dispositivo reside en el precio, ya que adquirir una tableta gráfica de calidad media sería necesaria una inversión de al menos 150€. Figura 1.1.1.1: Wacom Bamboo. Ejemplo de tableta gráfica. 8 INTRODUCCIÓN 1.1.2 Joystick Este dispositivo consiste en una palanca con dos o tres ejes. Originalmente fue creado para el control de ciertas partes de una aeronave pero en la actualidad este tipo de dispositivos se utiliza tanto para manejar helicópteros y aviones, manejar grúas y brazos mecánicos y controlar dispositivos teledirigidos (aeronaves, bombas guiadas, etc) como para controlar un ordenador o una videoconsola. En el último caso el uso del Joystick queda relegado a juegos en los que se simula el control de un avión, un helicóptero, una nave espacial o algún otro tipo de vehículo. Figura 1.1.2.1: Logitech Freedom. Ejemplo de joystick de tres ejes. 1.1.3 Wiimote Dispositivo inalámbrico dotado de infrarrojos, bluetooth y acelerómetro. Mediante una combinación de estos elementos permite el control del cursor como si de un puntero se tratara. Desde el lanzamiento en 2006 de la consola Wii de Nintendo no se ha parado de buscar funcionalidades para su controlador, desde como elemento de apoyo en presentaciones, hasta como base de un posible manejo futuro del 3D en los ordenadores. El problema como dispositivo es que exige una 9 INTRODUCCIÓN distancia y, en caso de usarse para realizar un trabajo continuo, su uso prolongado produce desgaste ya que no se utiliza en apoyo. Figura 1.1.3.1: Wiimote. 1.1.4 Novint Falcon Dispositivo a caballo entre el ratón y el joystick. Dispone de tres servomotores unidos a unas ballestas que se unen en una bola, mediante el manejo de estas se mueve el cursor. Está destinado a juegos en 3D, además dispone de un dispositivo en forma de pistola mediante el cual consigue más realismo. Los principales problemas son el precio, el hecho de que estando en juego para apuntar hacia un lado tengas que mover el dispositivo hacia el lado contrario y el tener que acostumbrarse el usuario al manejo de un dispositivo extraño. 1.1.4.1: Novint Falcon con el dispositivo Pistol. 10 INTRODUCCIÓN 1.1.5 Nueva generación de ratones En cuanto a ratones cabe destacar el intento por parte de Logitech con la creación del G9. Es un ratón que lleva una serie de contrapesos en su interior que provoca una sensación se oposición ante movimientos bruscos del usuario. Al funcionar mediante contrapesos, se trata de un sistema pasivo. Figura 1.1.5.1: G9 de Logitech. Se pueden apreciar los contrapesos. 1.1.6 Software En algunos videojuegos tratan la sensación de realismo mediante el propio software. Desde puntos de mira que se mueven a distinta velocidad dependiendo del arma que lleves, hasta dificultad en el manejo de un coche después de haber sufrido un accidente. Estas cosas ayudan, pero deberían de ir complementados con algún hardware. 1.2 Rigodón La propuesta de este proyecto es un ratón normal al que se le han añadido cuatro electroimanes y una base de hierro sobre la que se apoya. La idea de esta propuesta fue ser un dispositivo que engañase al cerebro con la sensación de que se está moviendo objetos de distinto peso (a mayor peso del 11 INTRODUCCIÓN objeto, mayor intensidad en los electroimanes y por lo tanto mayor dificultad de movimiento). El coste del dispositivo es muy bajo en comparación con otros productos, además es más versátil puesto que se puede aplicar a distintos campos de la ingeniería ya nombrados en la introducción. Figura 1.2.1: Esquema de Rigodón. En la imagen [Figura 1.2.1] se puede ver como están situados los electroimanes en el dispositivo. Del dispositivo salen tres cables, dos de ellos son conectores a puertos USB, uno de ellos el del ratón original, el segundo, es el que conecta el ordenador con el controlador de los electroimanes. En este proyecto nos interesa la parte que maneja este cable, el driver escrito controla la comunicación entre una aplicación y el microcontrolador de los electroimanes. El tercer cable se encarga de la alimentación, cada electroimán para llegar a la máxima potencia hay que aplicarle una intensidad de 12 voltios, por lo que con los 5 que proporciona el USB no sería suficiente. 12 INTRODUCCIÓN 1.3 Motivación Como se ha dicho en la introducción, tanto la industria en su más amplia definición, como el mercado de los videojuegos en concreto, son mercados muy amplios y con los que se puede negociar sobre la adquisición del dispositivo. Por un lado se podrían ahorrar costes sustituyendo las palancas en la industria por el ratón con force feedback. Supóngase una empresa dedicada al tratamiento de residuos químicos, y por alguna razón esa planta se ve obligada a tratar residuos orgánicos por un tiempo. Con las palancas se tendría que reconfigurar la máquina, con el coste que supondría, tanto en tiempo como en coste de la mano de obra del técnico. Con el ratón lo único que sería necesario hacer, sería cambiar la fuerza que se realiza sobre el ratón, un trabajo que podría hacer cualquier usuario, sin la necesidad de un técnico. En el mercado de los videojuegos con un ratón simple todas las armas que utilizan se utilizan igual, quedando como único diferenciador la velocidad a la que se mueva el punto de mira, o el simulador del retroceso de las armas que son controlados por software. Engañar al cerebro es fácil, y con solo poner una diferenciación de fuerza real necesaria para mover el arma es suficiente para que el usuario mejore su experiencia de juego, terminando con una sensación de realismo mayor que con un ratón convencional. También se encuentra una aplicación potencial al diseño gráfico. Sin lugar a dudas lo mejor a la hora de ponerse a dibujar en un ordenador en una tableta gráfica, pero hay un problema con estas, no todo el mundo se puede permitir gastarse en una herramienta para dibujar al menos 150€. Por ello para el usuario medio-bajo de programas de dibujo el ratón con force feedback es una buena solución. El principal problema a la hora de dibujar es que el ratón se escapa y estropea el trabajo ya realizado, este dispositivo pone una resistencia que hace que no pueda pasar eso, además, no solo se está usando como periférico de dibujo, también se usa como dispositivo de control del ordenador. 1.4 Objetivos El alcance del proyecto se centra en el desarrollo del software necesario para manejar un ratón especial, en concreto Rigodon, desarrollado en otro 13 INTRODUCCIÓN proyecto, con el objetivo de ofrecer al usuario una sensación de realismo mayor que el que tendría con el uso de un ratón comercial normal, tal y como se ha descrito en el apartado anterior de motivación. En los apartados siguientes se recogen las especificaciones requisitos, tanto del usuario como del software a desarrollar, y el análisis de los mismos, obteniendo como resultado el conjunto de funciones y aplicaciones a desarrollar para alcanzar dichos requisitos. 1.5 Requisitos de usuario Dado el dispositivo hardware descrito en los apartados anteriores, se debe hacer que funcione en un ordenador personal. Dada la naturaleza del dispositivo, se decidió que el sistema operativo al que debía ir dirigido debía ser Windows XP, en el siguiente punto se entrará más en detalle en por qué la elección de Windows XP. Es necesario desarrollar un software que controle el dispositivo. Mediante este se podría controlar el dispositivo de forma que se pudiera usar en aplicaciones que no lo tuvieran integrado, haciendo que así el dispositivo pueda ser utilizado de manera inmediata, de forma que el usuario no tenga que esperar a que los fabricantes lo adapten a cada uso. También se deberá adaptar un programa para la utilización del dispositivo, ya que se trata de un dispositivo cuya principal orientación es el ocio electrónico, se decidió que el programa que se adaptaría sería un videojuego. 1.6 Requisitos Software Los requisitos que debe cumplir el software que se vaya a desarrollar se pueden resumir en cinco puntos: Driver seguro: Cuando se escribe un driver la integridad del sistema operativo se pone en juego, ya que el “programa”, una vez terminado, se ejecutará en la memoria de kernel, es decir, en el núcleo del sistema operativo. Cuando esto pasa, la aplicación accede a zonas de memoria reservadas para el sistema operativo pudiendo provocar fallos que comprometan al sistema. Por esto, el que el driver sea seguro es una prioridad. Proyecto escalable: A la hora diseñar el dispositivo a parte de las 14 INTRODUCCIÓN funcionalidades que se le dieron, se pensaron posibles actualizaciones y mejoras para realizar en un futuro. Por eso, al diseñar el driver se debe hacer desde las perspectiva de una posible ampliación. Funcionamiento del driver en Windows XP: Aunque esta no sea la última versión de Windows, Windows Vista se vio que era poco eficiente hasta el punto que ya existe una nueva versión, el Windows 7. A la hora de realizar este proyecto, Windows 7 está en su primera versión, es decir, hasta que Microsoft no distribuya el Service Pack para Windows 7 se puede considerar al sistema operativo una versión beta. Además, Windows XP es el sistema operativo más extendido entre la gente que más juega. Por esto debe funcionar prioritariamente en Windows XP. Cabe decir que funcionando en XP, el driver debería funcionar tanto en Vista como en 7. Desarrollo de una aplicación de control: Se desarrollará una herramienta sencilla para que el usuario pueda controlar el dispositivo. Adaptación de un videojuego: Se modificará el código fuente de un videojuego para comprobar la sensación de realismo obtenida. El videojuego adaptado será el Doom, ya que tiene el código fuente liberado por sus creadores y es antiguo, de forma que el código a tratar es menor que en otras opciones barajadas como el Duke Nukem 3D. 1.7 Análisis de requisitos Tras el análisis de los requisitos del apartado anterior, se ha tomado la decisión de dividir el diseño en tres aplicaciones a desarrollar, tal y como se puede ver en el diagrama de flujo de datos que se muestra a continuación: Figura 1.7.1: DFD de los tres módulos que componen el proyecto. 15 INTRODUCCIÓN A continuación se describe cada una de estas aplicaciones, y cuáles han sido los condicionantes estudiados para tomar la decisión final: 1> Driver para controlar el ratón. Esta función es la principal y más importante de todo el proyecto, ya que condiciona el resto de desarrollos a realizar. Hay varias razones por las que se decide hacer un driver. Se debía hacer funcionar un dispositivo desde dos aplicaciones, por lo que había principalmente dos opciones, la primera desarrollar un driver y utilizar las funciones creadas para manejarlo, la segunda era hacer funcionar el dispositivo utilizando la API del lenguaje de programación que tocase. Al tener que hacer funcionar el dispositivo en dos aplicaciones, la solución más óptima era desarrollar el driver e invocarlo desde las aplicaciones. La principal razón de la toma de esta decisión es que el dispositivo se quiere comercializar, por lo que con el desarrollo de un driver, se puede presentar a las empresas interesadas de forma que solo tengan que utilizar las funciones ofrecidas por el driver para adaptar sus aplicaciones. 2>Aplicación de control. Se desarrollará una aplicación de permitirá controlar y probar el dispositivo desde la máquina desde la que se trabaja. 3>Interfaz para otras aplicaciones. Se desarrollarán los procedimientos o rutinas necesarias para que se pueda utilizar el dispositivo desde otras aplicaciones, ya sean nuevos programas a desarrollar, o bien aplicaciones existentes que quieran utilizar el ratón. 1.8 Metodología La metodología a utilizar es la metodología clásica o en cascada adaptada a las necesidades de desarrollo de este proyecto. Para ello se utilizaron las siguientes fases: FASE0: Pre-análisis FASE1: Análisis FASE2: Diseño FASE3: Implementación FASE4: Pruebas 16 INTRODUCCIÓN En la fase de pre-análisis se analizó la problemática general. En esta fase es en la que se decidieron los tres módulos especificados en el apartado 1.4 de este documento. Esta fase es la única que no se repitió, el resto de fases se repitió para cada uno de los tres módulos que componen este proyecto, en las siguientes líneas se aclarará qué se hizo en cada una de las fases para cada módulo. Lo referente módulo del driver está explicado en detalle en el capítulo 2 de este libro, así como todo lo referente a la aplicación de control y al videojuego se puede ver en los puntos 3 y 4 de este documento respectivamente. 1.8.1 FASE1: Análisis En el módulo correspondiente al diseño del driver se realizó un estudio de las frameworks proporcionadas por Microsoft así como de la posibilidades ya existentes que se ofrecían. A través de este análisis se decidió la alternativa del uso de winUSB. Este análisis llevó muchos meses del proyecto ya que la documentación es escasa y debido al tema, es algo complicada de entender. En lo que corresponde a la aplicación en el análisis se decidió que debía de ser una aplicación muy simple en la que no hubiera necesidad de un manual de instrucciones que ocupase más de una página, ya que como se explicó en la introducción el dispositivo pretende ser una solución a las complicadas configuraciones de los aparatos en la industria. La aplicación enviará un char al driver, para ello podrá utilizar un slider o una caja de texto. De está forma el usuario podrá controlar el dispositivo desde la aplicación para su uso en aplicaciones no adaptadas a este. En lo que al videojuego se refiere, la fase de análisis fue la más larga de todas ya que se tuvieron que analizar más de 16MB de ficheros hasta que se aislaron los ficheros a analizar y luego analizar más de 160 ficheros de código hasta aislar las funciones que interactuaban con las armas. Como se puede apreciar en los módulos del driver y del videojuego las tareas a realizar fueron tareas muy duras a pesar de que el resultado sea algo simple. Por supuesto, el resultado es tan simple debido al buen análisis realizado en esta parte, esto hizo que el resto de las tareas fuesen más llevaderas. 17 INTRODUCCIÓN 1.8.2 FASE2: Diseño La fase del diseño de la parte del driver vino definida por las necesidades del dispositivo por lo que las decisiones que se tomaron venían de la propia naturaleza del dispositivo, por lo que el hecho de que se use en forma de línea de comunicaciones viene condicionado por el dispositivo. La única parte en la que hubo libertad fue a la hora de enviar un char de 8bits en lugar del envío de una estructura de 4bits. En el diseño de la aplicación de control habría dos partes, el diseño gráfico y el diseño de la lógica, en lo que al diseño gráfico se refiere, tras un análisis en el que primaba la simplicidad se decidió que para su manejo intuitivo lo mejor sería un slider con el que cambiar la fuerza que debía ejercer el dispositivo, este iría acompañado por un campo de texto en el que poder cambiar la fuerza sin necesidad de seguir la progresión del slider. En cuanto a la lógica, como el lenguaje elegido fue java, se decidió que la forma de gestionarlo sería un jFrame que llevase todo el peso gráfico de la aplicación con una clase que gestionase las llamadas a un interfaz nativo que permitiese la comunicación con el sistema operativo. En lo que al videojuego se refiere, el diseño estaba ya hecho por el equipo que realizó el port del Doom, por lo que solamente se tomó la decisión de abrir y cerrar la conexión con el dispositivo cada vez que se necesitase, ya que se consideró más seguro que dejarla abierta y cerrarla al terminar la ejecución del videojuego. 1.8.3 FASE3: Implementación Tras la decisión del uso de winUSB, en la implementación de la parte del driver se desarrollaron paquetes de instalación para el dispositivo para todos las distribuciones de Windows desde Windows XP service pack 2. Así como un manual de instalación del paquete que se encuentra en el ANEXO C de este proyecto. En la implementación de la aplicación, la parte gráfica fue muy rápida, principal razón por la que se eligió java para desarrollarla, pero la carga recaía en la creación del fichero .dll que comunicaría la aplicación con el driver. Esta parte llevó bastante tiempo de realización principalmente debido 18 INTRODUCCIÓN a problemas con el DDK de Microsoft. En el videojuego, una vez localizadas la funciones sobre las que tratar la implementación sería rápida debido a que tendría que realizar exactamente las mismas rutinas que la aplicación, por lo que el código ya estaba, solo había que crear una librería y añadirla al código desde donde se realizarían las llamadas correspondientes. 1.8.4 FASE4: Pruebas Todas las pruebas a realizar dependen del dispositivo que se trata de implantar, como ya se dijo en la introducción la parte del dispositivo depende del otro proyecto, por lo que hasta que el dispositivo no se construya, la fase de pruebas quedará aplazada. La batería de pruebas preparadas empezará por la prueba del comportamiento del dispositivo en el ordenador, habrá pruebas de respuesta del dispositivo en caso de desconectarlo durante la ejecución de la aplicación y la conexión del dispositivo una vez iniciado la aplicación de control. También se probará el videojuego forzando el cambio de arma en las distintas situaciones en que este evento se puede dar. 1.9 Planificación En el gráfico se puede ver la planificación dividida por cada módulo del proyecto, como se puede ver durante el tiempo que dura cada módulo hay poco solapamiento ya que cada módulo ayuda al desarrollo del siguiente. Se puede ver que lo primero que se realizó fue el driver, con la realización de esta primera parte se conseguiría que el resto del trabajo fuese más fácil. En el proceso se usaron más de 6 meses en el análisis y la búsqueda de documentación sobre el desarrollo de drivers para Windows, se alargó tanto por que la información que existe es escasa y de dudosa procedencia, lo que hacía aun más duro el proceso de análisis y diseño. En lo que a la aplicación se refiere, se solapó con la parte del driver durante el desarrollo de la interfaz gráfica de usuario y la lógica de gestión de eventos, el resto de la aplicación no se pudo desarrollar hasta tener el driver en funcionamiento. 19 INTRODUCCIÓN El análisis de los ficheros correspondientes al código fuente del videojuego, se inició a la vez que se desarrollaba la lógica de la aplicación, quedando una vez terminada la aplicación la finalización del análisis y la introducción del código en el código del ZDoom. En paralelo a las fases se fue realizando la documentación, dejando la finalización de esta para el último mes del proyecto. 2009 Driver Aplicación Videojuego Documentación OCTUBRE NOVIEMBRE DICIEMBRE Figura 1.7.1: Planificación para 2009. 2010 Driver Aplicación Videojuego Documentación ENERO FEBRERO MARZO ABRIL MAYO JUNIO Figura 1.7.2: Planificación para 2010. 20 DISEÑO DEL DRIVER Parte2 DISEÑO DEL DRIVER 21 DISEÑO DEL DRIVER 2 DISEÑO DEL DRIVER En este capítulo se explicará el proceso mediante el cual se desarrolló el controlador del dispositivo, además, debido a la poca documentación existente sobre el tema, se ha escrito de forma que sirva como guía de apoyo para cualquiera que se disponga a desarrollar un driver para Windows. Se necesita un driver para Windows capaz de comunicar las aplicaciones con un dispositivo cuyas especificaciones técnicas están en el apartado 2.1, para ello se realizó un estudio de las tecnologías que Microsoft ofrece a los desarrolladores de drivers, apartados 2.2 y 2.3. 2.1 Diseño de I/O El diseño del hardware es parte de otro proyecto [GONZ09] pero en lo que a este proyecto atañe, el dispositivo está formado por un microcontrolador que recibe una cadena binaria de ocho bits para controlar cuatro electroimanes y va conectado a un ordenador por un puerto USB. En principio la cadena que recibía el dispositivo era de cuatro bits, pero los sistemas operativos direccionan a nivel de Byte y el tipo de dato mínimo usado por C++ es el char (8bits) , por eso se cambió a ocho bits para no desperdiciar los cuatro que quedarían sueltos. Con esta decisión el diseño se encaminó hacia uno de los objetivos, la escalabilidad. Los cuatro bits sobrantes en la primera versión son son utilizados, pero para versiones posteriores, se pueden usar como un código que, por ejemplo, haga que los electroimanes de la parte derecha estén apagados mientras los otros funcionan con normalidad. Este tipo de acciones se han denominado “efectos”. La aplicación solo necesita mandar la información, no recibe ninguna información por parte del dispositivo por lo que se trata de una línea de comunicación unidireccional. Para dar una visión general del circuito se añade el esquema general del circuito sacado del proyecto [GONZ09] y se añade una explicación general de cada una de las etapas por las que la cadena enviada pasa para que al final los 22 DISEÑO DEL DRIVER electroimanes del dispositivo se carguen. Figura 2.1.1: Esquema del circuito del dispositivo dividido por etapas. 23 DISEÑO DEL DRIVER 2.1.1 Etapa1: Recepción y tratamiento de los datos Figura 2.1.1.1: Etapa 1: Recepción y tratamiento de datos aislada. Esta etapa es la encargada de recibir los datos, la parte más importante de la etapa es el microcontrolador PIC16F84A, este recibe los datos en serie del USB en el puerto RA0, y tiene el puerto RA1 ya preparado para en versiones posteriores mandar información al ordenador que lo controle a modo de mensajes de confirmación o alerta. Dentro del micro, dependiendo del valor que se reciba se buscará en una tabla la tensión necesaria correspondiente al valor que se reciba y se enviará por medio del puerto RB en paralelo al conversor digital-analógico. También contiene el reloj del circuito conectado al circuito en los puestos de entrada y salida del reloj. Se aprovechan los 5 voltios que provee el USB para alimentar el microcontrolador y el LED que indica que el dispositivo recibe alimentación tras pasar por un regulador de tensión. 24 DISEÑO DEL DRIVER 2.1.2 Etapa2: Conversión digital-analógica Figura 2.1.2.1: Etapa2: Conversión digital-analógica aislada. En esta etapa el conversor digital-analógico, DAC0808, recibe un código de 8 bits es paralelo y los convierte en un valor entre 0 y 12 voltios que se enviará a la siguiente etapa. Este proceso se realiza en 150 ns. 2.1.3 Etapa 3: Amplificación/buffer Figura 2.1.3.1: Etapa 3: Amplificación/buffer aislada. Esta etapa sirve para aislar las etapas 1 y 2 de la etapa 4, ya que una caída brusca de tensión en la etapa 4 podría dañar el resto del circuito. Además al estar realizado con un amplificación operacional se podría adaptar su uso a la amplificación en posibles desarrollos futuros. 25 DISEÑO DEL DRIVER 2.1.4 Etapa 4: Salida Figura 2.1.4.1: Etapa 4: Salida aislada. Cosiste en un transistor MOSFET que regula la tensión de los imanes como si se tratase de uno solo, la corriente la recibe de la fuente de corriente auxiliar, en este caso el dispositivo va conectado a la red eléctrica por medio del cable de alimentación del dispositivo. Es una visión general del circuito, si se desea entrar en más detalle consúltese el proyecto “Control Intuitivo de Ratón por Force Feedback” de donde se sacaron los gráficos que acompañan a esta explicación [GONZ09]. 2.2 UMDF y KMDF El modelo de la WDF (Windows Driver Foundation) provee dos frameworks con las que trabajar según convenga al tipo de dispositivo para el que se esté escribiendo el driver UMDF(User Mode Driver Framework) y KMDF (Kernel Mode Driver Framework). El sistema operativo Windows divide su memoria virtual en dos partes, User Mode y Kernel Mode. Todos los procesos que se ejecutan en Windows se ejecutan en modo usuario (User Mode) excepto el proceso System. En modo usuario los procesos no pueden 26 DISEÑO DEL DRIVER gestionar interrupciones ni tienen acceso directo al hardware, por lo que estos procesos para comunicarse con un dispositivo físico necesitan de algo que haga de pasarela, es el llamado reflector que se explica en la sección 2.2.1. En Kernel Mode, los procesos tienen acceso directo al hardware, a la memoria y pueden realizar llamadas al sistema, por esto y la posibilidad de manejar la paginación de la memoria, la programación el modo Kernel es peligrosa para la integridad del sistema. Tras el estudio de ambas frameworks se llegó a la conclusión de que la mejor opción era usar un UMDF, la principal razón fue que como la parte que se quiere controlar es la parte de los cuatro electroimanes, no es necesaria la gestión de interrupciones, por lo tanto lo que se necesita es una especie de línea de comunicación entre el ordenador y el dispositivo. Esto se puede hacer usando cualquiera de las dos frameworks, pero al escribir un UMDF se termina con uno de los problemas principales de los drivers y a la vez uno de los objetivos del proyecto, la estabilidad del sistema operativo. Al ser ejecutado en modo usuario, es el sistema operativo el que se ocupa de gestionar todas las funciones del kernel que se necesiten usar (memoria, paginación, etc) de forma que si el dispositivo falla, en lugar de aparecer el famoso Blue Screen of Death, que fuerza a reiniciar el sistema para recuperar la estabilidad poniendo en peligro toda la sesión de trabajo y partes del sistema, simplemente el dispositivo dejaría de funcionar, dejando que el sistema operativo siga funcionando sin problemas. Con un KMDF se necesitan dos máquinas para poder comprobar los posibles fallos de ejecución que pudiera tener, mientras que con el UMDF con una sola máquina se pueden depurar esos errores, facilitando así el proceso de depuración de errores. 2.2.1 Infraestructura de UMDF Para hacer más fácil la comprensión de como funciona un driver UMDF, se explicará apoyándose el la imagen a continuación [Figura 2.1.1.1], empezando desde la parte de la aplicación. Application: Lo primero que ocurre es que la aplicación desde el modo usuario invoca al driver, para ello se vale de las funciones proporcionadas por Windows API. 27 DISEÑO DEL DRIVER Windows API: Proporciona una serie de funciones que permiten la comunicación entre las aplicaciones (Modo Usuario) y los Subsistemas del Kernel (Modo Kernel). Kernel Subsystems: Son pequeños subsistemas proporcionados por el Kernel, tales como el I/O Manager o el Plug and Play Manager (PnP Manager). Estos se valen de IRPs (I/O Request Packets) para comunicar las peticiones al Reflector. Figura 2.1.1.1: Infraestructura UMDF. Reflector: Es el alma de la infraestructura de los UMDF. El objetivo de una IRP debe de ser un KMDF device object, los cuales se encuentran en Modo Kernel. Los UMDF no pueden acceder al espacio de direccionamiento de Modo Kernel, por eso se vale del Reflector, el Reflector es un driver KMDF que representa a los drivers UMFD en el Modo Kernel. Además controla las comunicaciones que recibe y envía el UMDF Host Procress, el cual está en modo usuario. 28 DISEÑO DEL DRIVER Para realizar estas funciones el reflector crea tres objetos: Up Device Object, Down Device Object y Control Device Object. Up Device Object: Es el encargado e recibir los IRPs del I/O Manager y enviarlos al Host Process correspondiente para que los procese, una vez procesadas las devuelve al I/O Manager. Down Device Object: Se encarga de enviar a Lower Device Stack la I/O Request ya procesada. También se comunica con el UMDF Host Process para distintas cosas como puede ser crear una Device Interface. Control Device Object: Maneja la comunicación entre el Reflector y el UMFD Host Process que no tiene que ver con I/O Requests, como la creación o la finalización de los Host Process. UMDF Host Process: Es el encargado de cargar el driver y la librerías Dll. Se encarga de la comunicación entre los drivers UMDF y el Reflector. Dentro del UMDF Host Process se encuentra la UMDF Stack e instancias de Framework. UMDF Stack: Cada Driver UMDF está formado por varios drivers del tipo Filter Driver o Function Driver, estos se colocan en una pila (UMDF Stack), esta pila controla y procesa las I/O Requests. Framework: Para cada Driver en la UMDF Stack hay asignada una instancia de Framework. Se trata de una librería de sostiene el UMDF Object Model y que comunica los drivers con el HostProcess. Driver Manager: Maneja todos los UMDF Host Process en el sistema, los crea, mantiene su estatus y los finaliza una vez no sean necesarios. Lower Device Stack: Se encarga del la comunicación con el dispositivo. En el caso de este proyecto, se puede usar la Kernel Mode Device Stack de Microsoft, ya que se trata de un dispositivo USB, pero en algunos casos podría ser necesario la creación Lower Drivers por parte del vendedor. Device: El dispositivo receptor de la I/O Request. 29 DISEÑO DEL DRIVER 2.3 WinUSB Desde la aparición de Windows Vista, la Windows Driver Foundation añadió al DDK un driver llamado WinUSB.sys. Este driver facilita la creación de drivers para dispositivos USB. Es un driver KMDF, WinUSB.sys, que proporciona una librería, Winusb.dll, que permite su uso desde aplicaciones como si se tratase de un driver UMDF. WinUSB permite la adaptación de un driver existente a las necesidades de un dispositivo específico mediante un fichero de instalación .inf (ver ANEXO A para ver ejemplo). No es válido para cualquier dispositivo USB, tiene que cumplir las siguientes condiciones: - No soporta el tráfico isócrono. - No puede acceder más de una aplicación a la vez. El dispositivo cumple ambas condiciones, ya que el tráfico que necesitamos para el dispositivo nunca debe ser isócrono, y por la naturaleza del dispositivo, solo va a estar funcionando con una aplicación a la vez. Ya sea la de control o el videojuego, no tiene sentido estar con las dos aplicaciones a la vez. WinUSB funciona para cualquier sistema operativo Windows a partir de Windows XP Service Pack 2, el problema es que para cada distribución de Windows y cada bus de datos, el fichero de instalación y los instaladores que le acompañan son distintos, para solucionar esto en el CD adjunto a este proyecto se facilitan los distintos paquetes de instalación para cada distribución existente hasta la fecha partiendo desde Windows XP Service Pack 2. Este driver facilita mucho el trabajo a la hora de tener un driver operativo, en la programación de la aplicación la diferencia es mínima. Solo se tiene que escribir un poco más de código y usar las funciones que facilita la librería <winusb.h>. Para ver un ejemplo de su funcionamiento ir al apartado 3.5 o al ANEXO B para ver el código. Esta fue la solución final que se dio a esta parte del proyecto, además de estas razones, al usar un driver de Microsoft se puede dejar de lado como problema la estabilidad del driver. 30 DISEÑO DEL DRIVER 2.3.1 Install.inf Es el fichero de instalación de WinUSB. Ver ANEXO A para ver un ejemplo. Requiere una serie de campos entre los que destacan los que se tienen que modificar para cada driver y para cada distribución: ClassGUID: es el GUID, Globally Unique Identifier, que indica el tipo de dispositivo que se va a usar. DeviceInterfaceGUIDs: el GUID del dispositivo, es un numero aleatorio que identifica al dispositivo en este caso el GUID elegido ha sido AE5F20A3-09D7-4E62-B2F7-3D5172853709 y ha sido genera mediante la aplicación proporcionada por guidgenerator.com. CoInstallers_AddReg: Aquí se añaden los coinstaladores dependiendo de la distribución para la que se esté adaptando el fichero de instalación. El resto de las cosas se pueden dejar tal y como están para usar un dispositivo del estilo del que maneja este proyecto, si algún dispositivo tiene otras necesidades distintas se podrían modificar los campos. 2.3.2 Funciones en <winusb.h> Winusb proporciona una serie de funciones que se pueden encontrar en la página web de Microsoft para desarrolladores: WinUsb_AbortPipe: Cancela todas las transferencias pendientes en una pipe. WinUsb_ControlTransfer: Transmite datos de control sobre un endpoint de control por defecto. WinUsb_FlushPipe: Descarta cualquier dato en una pipe. WinUsb_Free: Libera los recursos reservados por WinUsb_Initialize. WinUsb_GetAssociatedInterface: Guarda un controlador para un interfaz asociado. WinUsb_GetCurrentAlternateSetting: Devuelve el marco actual de un interfaz alternativo para un interfaz. WinUsb_GetDescriptor: Devuelve el descriptor que se pide. WinUsb_GetOverlappedResult: Devuelve un HRESULT con la 31 DISEÑO DEL DRIVER coincidencias. WinUsb_GetPipePolicy: Devuelve la política de una pipe (endpoint). WinUsb_GetPowerPolicy: Devuelve la política de uso de la energía de un dispositivo. WinUsb_Initialize: Crea una controlador de WinUsb para el dispositivo especificado por un controlador de fichero. WinUsb_QueryDeviceInformation: Consigue información sobre el dispositivo físico asociado a un controlador de WinUSB. WinUsb_QueryInterfaceSettings: Consigue el descriptor del interfaz de un marco actual de un interfaz alternativo para un controlador de interfaz. WinUsb_QueryPipe: Devuelve información sobre una pipe asociada con un interfaz. WinUsb_ReadPipe: Lee los datos en una pipe. WinUsb_ResetPipe: Reinicia el interruptor de datos y limpia la condición de atasco en una pipe. WinUsb_SetCurrentAlternateSetting: Modifica el marco alternativo de un interfaz. WinUsb_SetPipePolicy: Modifica la política de una pipe (endpoint). WinUsb_SetPowerPolicy: Modifica la política de uso de la energía de un dispositivo. WinUsb_WritePipe: Escribe los datos en una pipe. Como funciones interesantes destacar WinUsb_WritePipe, es la función mediante la cual envía datos una aplicación al driver y este al dispositivo. 2.4 Tecnología Para el la adaptación del driver se usó de Driver Development Kit. Este kit es facilitado por Microsoft de manera gratuita siempre que se posea una licencia válida de alguna de las distribuciones de Windows. En este kit se proporcionan las frameworks necesarias para realizar tanto un driver de modo usuario como uno de modo kernel. También se facilitan los instaladores de WinUSB así como las librerías necesarias para su uso. 32 DISEÑO DEL DRIVER El kit es descargable desde la página web de la Microsoft Development Network [1]. 33 DISEÑO DE LA APLICACIÓN DE CONTROL Parte3 DISEÑO DE LA APLICACIÓN DE CONTROL 34 DISEÑO DE LA APLICACIÓN DE CONTROL 3 DISEÑO DE LA APLICACIÓN DE CONTROL En este capítulo se tratará el diseño de la aplicación que el usuario utilizará a modo de panel de control del dispositivo, durante la sección se entrará en detalle en la interfaz de usuario, en cómo se usa en la aplicación la Java Native Interface, así como en el programa a implementar que comunica la aplicación con el dispositivo. La aplicación de control es una aplicación de usuario con interfaz simple, en la que se podrá modificar la fuerza que deberá hacer el dispositivo. Para la realización de la aplicación se utiliza eclipse con su framework para el lenguaje de programación Java. Esta aplicación se desarrolla para que se pueda modificar la fuerza del dispositivo y se pueda usar en aplicaciones en las que no está integrado, es decir, si un usuario quiere fijar una fuerza determinada para usar alguna herramienta de dibujo, este puede fijarla en la aplicación y luego seguir trabajando en la herramienta de dibujo. Figura 3.1.1: Aplicación de prueba al iniciar y al modificar uno de los valores. 35 DISEÑO DE LA APLICACIÓN DE CONTROL 3.1 Componentes La aplicación [Figura 3.1.1]está compuesta principalmente por un JTextField y un JSlider, dando así la posibilidad de cambiar la fuerza del dispositivo de dos maneras distintas. 3.1.1 JSlider jFuerza Este es el principal componente gráfico de la aplicación ya que pretende ser el principal controlador. Comprende el intervalo desde cero hasta quince, ambos incluidos. La modificación es progresiva, para llegar de dos a siete tiene que pasar por el tres, el cuatro, el cinco y el seis. Cuando se modifica jFuerza el campo de texto txtFuerza también se modifica de forma que siempre habrá el mismo valor en los dos campos. Al mover la posición de la fuerza en el jSlider se crea una instancia de la clase DriverCall.java que se encarga de comunicar con el driver. 3.1.2 JTextField txtFuerza Es la alternativa a jFuerza, se usará si se quiere un salto no progresivo. Al modificar este campo, jFuerza se actualiza de la misma manera que se ha explicado en la sección anterior. Al igual que pasaba con el jSlider, cuando se modifica la fuerza en el campo, se crea una instancia de la clase DriverCall.java que se encarga de comunicar con el driver. 3.2 JNI (Java Native Interface) La elección de Java como lenguaje de desarrollo para la aplicación se debió a que era un lenguaje conocido y a que el desarrollo de la interfaz de usuario sería muy rápido. El problema era que Java no puede comunicarse directamente con el driver, por lo que se usó la librería JNI que provee Java, mediante ella se puede ejecutar código C desde una aplicación Java. Para esto se usaron Test.java, como clase Java que controla el flujo de la aplicación mediante la interfaz gráfica; DriverCall.c, es un fichero que contiene el código de invocación del driver; y 36 DISEÑO DE LA APLICACIÓN DE CONTROL DriverCall.h, es la librería que utiliza JNI, para ejecutar DriverCall.c como si formase parte de Test.java. Para ello Test.java se apoya en la clase DriverCall.java que se explica en el punto 3.3. Como se ve en la imagen [Figura 3.1.2], mediante JNI, Test.java se comunica con el driver enviándole una fuerza previamente introducía por el usuario, mediante el jSlider o el jTextField, el driver recibe esa información, la procesa y se la envía al dispositivo. Figura 3.2.1: Diagrama de funcionamiento de la aplicación. 3.3 Clase DriverCall.java Esta clase es invocada desde Test.java. A partir de esta clase se crea la librería DriverCall.h, que es usada posteriormente en DriverCall.c para crear el .dll que usa la aplicación para invocar las funciones de escritura en el driver. La razón de usar esta clase es que para lanzar la llamada a la función nativa hay que crear una instancia de la clase que la define, por lo que si se crea una nueva instancia de Test.java, se abriría otra jFrame con el mismo contenido que la primera cada vez que se cambie la fuerza. 3.4 Salida En la salida de la aplicación se debe realizar una última llamada al driver que deja la fuerza a cero. Tal y como está diseñada la aplicación, es necesaria esta llamada ya que si 37 DISEÑO DE LA APLICACIÓN DE CONTROL no se realiza, el dispositivo seguiría ejerciendo fuerza hasta que se reiniciase la aplicación y se pusiera a cero. Con esta última llamada, se realiza una especie de proceso de limpieza que dejaría al dispositivo funcionar como un ratón estándar. 3.5 DriverCall.c Es ejecutada desde la aplicación mediante JNI. Para ver el código completo de DriverCall.c consúltese ANEXO B. Todas la funciones excepto Java_DriverCall_driverCall, devuelven un BOOL que se usa de control en la función principal. 3.5.1 Java_DriverCall_driverCall Es la función principal, desde esta se controla la llamada al resto de las funciones. Recibe datos desde Test.java, pero lo interesante es el jint i que se recibe. La función convierte i en envio, un char que se será el que se envíe al dispositivo. Para poder enviarlo antes se tiene que crear un fichero que maneja el driver, crear un pipe que enlace con el dispositivo y por último, enviar el char. Para realizar esta conversión se utiliza un switch() sobre el jint i, la razón de la conversión mediante switch es la incompatibilidad de tipos de datos, ya que jint es un entero de 32 bits, mientras que la variable en la que se quiere meter es un char que en el lenguaje de programación C es una variable de 8 bits. Para evitar esta incompatibilidad se usa el switch de modo que se para cada valor entre 0 y 15 se mete ese mismo valor en el char. Lo que se mete en realidad son los valores hexadecimales de 0 a 15 de forma que cuando se envíen al dispositivo el microcontrolador de este pueda interpretarlos como debe. Por último esta función cerrará el fichero de control mediante las funciones CloseHandle y WinUsb_Free, la segunda contenida en winusb.h. CloseHandle(hDeviceHandle); WinUsb_Free(hWinUSBHandle); 38 DISEÑO DE LA APLICACIÓN DE CONTROL Por último devuelve el control a Test.java. 3.5.2 GetWinUSBHandle Para abrir el fichero que maneja WinUSB, se necesita la función CreateFile, está función se usa únicamente para conseguir la interfaz de control de WinUSB. Para esto, lo que hace esta función es invocar a la función GetDeviceHandle. Donde se modifica phWinUSBHandle, declarada en la función principal. 3.5.3 GetDeviceHandle Es la que definitivamente crea el fichero que controla WinUSB. Esto lo hace después de pasar un protocolo que facilita Microsoft, en este protocolo se hacen una serie de comprobaciones, una vez pasadas todas por fin se ejecuta la función CreateFile. *hDeviceHandle = CreateFile (lpDevicePath, GENERIC_WRITE,FILE_SHARE_WRITE,NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); Se puede ver en la función que se ha abierto únicamente en modo escritura ya que el dispositivo no necesita las operaciones de lectura. Además debe ser invocada antes de llamar a GetWinUSBHandle, ya que el valor que se pasa WinUSBHandle a esta en hDeviceHandle es el que se consigue tras una llama a esta función. 3.5.4 QueryDeviceEndpoints Una vez conseguido el fichero para manejar el driver, lo siguiente es abrir una línea de comunicaciones. En esta función, básicamente lo que se hace es conseguir un identificador (pipeid) del end point sobre el que hay que escribir. 39 DISEÑO DE LA APLICACIÓN DE CONTROL 3.5.5 WriteToBulkEndpoint Esta función es la hace la escritura. En ella se define un tamaño de buffer, que es este caso el del tamaño de un char. Y ese buffer es el que se pasa al driver por medio de la función después de llenarlo con la variable envío. Para ello se usa la función WinUsb_WritePipe que es proporcionada en winusb.h para mandar el contenido del buffer al pipe que apunta a nuestro end point: bResult = WinUsb_WritePipe(hDeviceHandle, *pID, szBuffer, cbSize, &cbSent, 0); Después devolverá el control a la función principal donde se cerrará el fichero que maneja el Driver. 3.6 Tecnología La aplicación se divide en dos partes, la parte gráfica y la parte lógica, que es la que llama al driver. Para la parte gráfica se utilizó el lenguaje de programación Java y Eclipse Galileo como framework de este. La elección de estos se debe a que se habían utilizado de antemano y facilitaban un desarrollo muy rápido del GUI. Para la lógica se usó el lenguaje de programación C y como framework el Microsoft Visual Estudio Express Edition, ya que este a parte de trabajar con C nos permitiría trabajar con C++ en otras partes del proyecto. Podría haberse utilizado la misma framework para ambos lenguajes, pero ambas herramientas son gratuitas por lo que no incrementan el coste del proyecto y facilitan la abstracción al tener frameworks distintos para cada lenguaje. 40 ADAPTACIÓN DE VIDEOJUEGO Parte4 ADAPTACIÓN DE VIDEOJUEGO 41 ADAPTACIÓN DE VIDEOJUEGO 4 ADAPTACIÓN DE VIDEOJUEGO Durante este capítulo se explicará como se adaptó un videojuego a su uso con el ratón, desde como fue la elección del videojuego, hasta como se inyectó el código concerniente al manejo del driver en el código proporcionado por el equipo de desarrollo del port del Doom para Windows ZDoom. Ante la intención de entrar en el mercado de los videojuegos se decidió que se debía adaptar un videojuego del tipo FPS (First-Person Shooter), de forma que se pudiera hacer una demostración. Ya que se trataba de una versión de demostración no comercializable, solo se trataría en el aspecto de las armas, sin entrar en la parte de dificultar el movimiento cuando cambias a un entorno distinto como puede ser el agua. 4.1 Elección de videojuego A la hora de elegir qué videojuego adaptar, se realizó un búsqueda de códigos de videojuegos que habían sido liberados, tras una primera criba, quedaron dos opciones, el Doom y el Duke Nukem 3D. Con un análisis relativamente rápido se llegó a la conclusión definitiva de que el videojuego a tratar debía ser el Doom. La principal razón de esta elección fue la diferente carga de trabajo que suponía cada uno, ya que el código del Doom, al ser uno de los primeros First Person Shooters, era mucho más reducido que el del Duke Nukem 3D. En la decisión también tomó parte la nostalgia y los recuerdos del tiempo gastado durante la infancia, por parte de los creadores del dispositivo, jugando al Doom. El código que liberó IDSoftware estaba preparado para entornos Linux, por lo que para adaptar se usó un port para Windows de este juego llamado ZDoom. En este port se encuentran todos los juegos que utilizan el motor del Doom (Doom engine), como por ejemplo Hexen, Heretic, Doom 2 y por supuesto el Doom que es el que atañe a este proyecto. 42 ADAPTACIÓN DE VIDEOJUEGO 4.2 Ingeniería inversa La ingeniería inversa consiste en el estudio de una tecnología existente hasta comprender el funcionamiento del dispositivo sobre el que se está realizando el estudio. En este caso al tratar con un videojuego, el proceso de ingeniería inversa se realizaría sobre el código fuente liberado por IDSoftware y distribuido por el equipo del ZDoom en lugar de sobre un dispositivo. En una primera fase se analizaron los ficheros para aislar el código común a todos los videjuegos y el código del Doom en un már de ficheros de más de 16MB. Una vez aislada esa parte del código, quedaron ciento cincuenta y cuatro ficheros entre librerías y código de C++. Estos ficheros se analizaron hasta que se aisló un solo fichero que manejaba el cambio de armas, el fichero p_pspr.cpp. Dentro de este fichero se localizó la función sobre la que se trabajaría, A_WeaponReady. DEFINE_ACTION_FUNCTION(AInventory, A_WeaponReady) { player_t *player = self->player; AWeapon *weapon; if (NULL == player) { return; } weapon = player->ReadyWeapon; /* Llamadas a las funciones */ if (NULL == weapon) { return; } // Change player from >InStateSequence(self->state, attack state if (self- self->MissileState) || self->InStateSequence(self->state, self->MeleeState)) { static_cast<APlayerPawn *>(self)->PlayIdle (); } // Play ready sound, if any. if (weapon->ReadySound && player->psprites[ps_weapon].state == weapon43 ADAPTACIÓN DE VIDEOJUEGO >FindState(NAME_Ready)) { if (!(weapon->WeaponFlags & WIF_READYSNDHALF) || pr_wpnreadysnd() < 128) { S_Sound (self, CHAN_WEAPON, weapon->ReadySound, 1, ATTN_NORM); } } // Prepare for bobbing and firing action. player>cheats |= CF_WEAPONREADY; >psprites[ps_weapon].sx = 0; playerplayer- >psprites[ps_weapon].sy = WEAPONTOP; } Se ha marcado como código comentado el lugar donde se incluiría la llamada a las funciones. 4.3 Adaptando el código Una vez localizadas las funciones, se realizó el mismo proceso que en DriverCall.c explicado en el apartado 3.5 de este documento. Había dos opciones: crear el fichero de control en el inicio de la ejecución y cerrarlo al finalizar la partida; o abrir y cerrar cada vez que se realizase un cambio de arma. Se decidió la segunda opción porque se vio mejor que la primera, ya que aunque la eficiencia de realizar el proceso cada cambio de arma era menor, era mucho más respetuoso con la memoria, ya que la libera cuando ya deja de ser necesaria. Teniendo en cuenta que en juego real no cambias de arma todo el rato, se decidió que se llamaría a la rutina de la misma manera que en DriverCall.c cada vez que se cambie el arma. Para esto se realizó una librería con las funciones necesarias de llamada, de forma que solo incluyéndose en el código se pudiesen llamar sin ensuciar el código proporcionado. #include “rigodon.h” Rigodon.h es el nombre que recibió la librería utilizada en la realización de 44 ADAPTACIÓN DE VIDEOJUEGO este proceso. Además se tuvieron que incluir las librerías y tipos de datos especiales que se usan en las funciones que realizan el proceso de escritura en el driver. 4.4 Tecnología Como recurso principal para esta parte del proyecto se utilizó el código fuente del port del Doom llamado ZDoom [2]. ZDoom contiene una clausula en su web que permite distribuir el videojuego siempre que sea de manera gratuita. El código del ZDoom es un código C++, por lo que para su tratamiento y modificación se utilizo el framework Microsoft Visual Estudio Express Edition. 45 CONCLUSIONES Y TRABAJOS FUTUROS Parte5 CONCLUSIONES Y TRABAJOS FUTUROS 46 CONCLUSIONES Y TRABAJOS FUTUROS 5 CONCLUSIONES Y TRABAJOS FUTUROS En este capítulo se comentarán las dificultades y las conclusiones a las que se llegó durante el desarrollo de este proyecto, así como las posibilidades de trabajo que aparecen con la consecución de este proyecto. Cuando se encara el desarrollo de un driver de las características del de este proyecto, se debe tener en cuenta que la mayoría del tiempo del proyecto se va consumir en el análisis y el diseño del controlador, quedando la programación prácticamente en un segundo plano. Una de las cosas que se infravalora a la hora de la programación son los comentarios, la importancia se ve cuando se tiene que modificar un código ajeno. Un código bien comentado puede ahorrar muchísimo trabajo a los que tengan que venir después a trabajar con el código. Cuando se decidió hacer este proyecto, parecía un desarrollo sencillo, pero según se profundiza en él se encuentran matices en los que se ve que, aún siendo un proyecto para un prototipo simple de control de electroimanes, se puede complicar hasta extremos insospechados. Usando exactamente la misma tecnología, se pueden sustituir los imanes por servomotores e implantarlos en un joystick, se podrían llevar a un nivel superior periféricos hoy en día casi en desuso. No se pueden olvidar los 4 bits que no se llegan a utilizar de la cadena que se manda al dispositivo, con una adaptación sencilla y una modificación de la alfombrilla de hierro sobre la que se apoya se podría llegar a una segunda versión del dispositivo en la que se añadiesen efectos. Por ejemplo dejando solo ciertos puntos de la plancha con hierro (suficientes para que la sensación fuerza siquiera siendo potente) y que ante algún evento del juego solo un electroimán puesto a la máxima potencia dificultase ciertos giros (un pinchazo mientras conduces un vehículo por ejemplo). Además en los periféricos para ordenador actuales se puede apreciar la falta de complementos que ya están disponibles actualmente en las consolas de videojuegos, como pueden ser la vibración o un giróscopo, que bien adaptados podría ser un gran avance; solo hay que imaginarse el poder cambiar la 47 CONCLUSIONES Y TRABAJOS FUTUROS orientación de una textura en un programa de tratamiento de imágenes con un simple giro de muñeca. Tampoco se puede dejar de lado una de las motivaciones principales de la realización de este proyecto, el manejo de brazos mecánicos en la industria, se podría realizar una aplicación que funcionase como interfaz más compleja entre el operario y la máquina. El desarrollo del videojuego se ha dejado un como demostración simple y el dispositivo solo interactúa con las armas, por lo que la adaptación completa de un videojuego o la creación de un videojuego en el que se explote en el dispositivo sería una idea para un proyecto en el futuro. Se puede apreciar un amplio abanico de posibilidades a partir de una idea relativamente básica. Por supuesto, el principal trabajo futuro será la comercialización del dispositivo, ya que es una de la principales motivaciones de este. 48 ESTUDIO ECONÓMICO Parte6 ESTUDIO ECONÓMICO 49 ESTUDIO ECONÓMICO 6 ESTUDIO ECONÓMICO En este capítulo se incluye un pequeño análisis de los mercados a los que va dirigido el dispositivo, así como el presupuesto del proyecto desglosado por secciones de trabajo y coste de las horas/hombre. 6.1 Estudio de mercado Como se trata de un proyecto nuevo, es difícil saber como puede reaccionar el mercado. Tanto el la industria, como la informática en general, en concreto en sector de los videojuegos, están abiertos a mejoras tecnológicas. Por un lado están los brazos mecánicos que maneja la industria. Estos tienen deficiencias en el tanto en control manual, dejado en manos del control numérico la mayoría de tareas, como a la hora del reciclaje de estos, ya que para un cambio de tarea, deben cambiar la configuración directamente en la máquina. Por el otro lado están la informática y los videojuegos. En la informática general el dispositivo iría dirigido al usuario amateur de programas profesionales, para los programas de diseño gráfico como FreeHand o Photoshop, o programas de dibujo de planos como Autocad que requieren gran precisión en ciertos momentos, ya existen tabletas gráficas que cubren las necesidades de profesionales dispuestos a gastarse lo que cuesta una tableta gráfica con el fin de mejorar su trabajo. Pero el usuario que diseña o hace planos por afición no necesitará tanta precisión ni estará dispuesto a gastar ese dinero. Ese sector sería el que habría que abordar en lo que a la informática general se refiere. En cuanto al mercado de los videojuegos se puede decir que es todavía más incierto si cabe. Por un lado está el hecho de que mueve más del 50% del ocio audiovisual, como ejemplo, en diciembre de 2009, el juego Call of Duty Modern Warfare 2 hizo más dinero que la taquillera película Avatar. Pero por el otro lado muchos jugadores no buscan realismo en un dispositivo si no eficiencia, estos son los jugadores de juegos online, que buscan ser más rápidos y mejores que el contrario. El sector de usuarios al que tendría que dirigirse es a los jugadores que buscan calidad gráfica y gran interacción con el entorno, realismo al fin y al cabo. La competencia en el mercado de los videojuegos es muy grande y no se 50 ESTUDIO ECONÓMICO rige por el criterio del precio, ya que los grandes fanáticos de los videojuegos están dispuestos a gastar mucho dinero en periféricos que mejoren la experiencia de juego. El coste de construir un solo dispositivo se calcula en 32,21€, pero una vez iniciada la producción en cadena y la compra de elementos al por mayor, se calcula que no pasaría de los 15€. Comercializándolo a 35€ se estaría sacando un 133% de beneficio y se estaría muy por debajo del precio de los periféricos orientados a profesionales de los que se ha hablado antes. Si al precio se le añade una buena campaña publicitaria, se considera que el dispositivo tiene una posibilidades de éxito bastante altas. 6.2 Presupuesto El presupuesto fue analizado desde un punto de vista humano, es decir, el coste de las personas en cada fase del proyecto. Esto es así porque no se ha necesitado ninguna inversión en software o hardware que no estuviera adquirido de antemano ya que la herramienta principal (WDK) se facilita a cualquier usuario con una licencia de Windows. El resto se realizo con aplicaciones gratuitas y open source, como Visual C++ Xpress Edition o Eclipse Galileo. El presupuesto queda desglosado en las siguientes tablas [Figuras de 6.1 a 6.3]]: Tiempo dedicado (horas) DIRECCIÓN ANÁLISIS DISEÑO DESARROLLO DOCUMENTACIÓN TOTAL Driver 10 50 30 33 15 138 Aplicación 3 7 8 15 10 43 Videojuego 3 20 5 10 8 46 Total 16 77 43 58 33 227 Figura 6.1: Horas de trabajo por grupo de trabajo y sección del proyecto. TITULACIÓN COSTE €/h Director de proyecto Analista Programador 60,00 € 40,00 € 30,00 € Figura 6.2: Coste por hora de trabajo dependiendo del puesto. 51 ESTUDIO ECONÓMICO SECCIÓN DIRECCIÓN ANÁLISIS Y DISEÑO DESARROLLO DOCUMENTACIÓN TOTAL COSTE €/h 960,00 € 4.800,00 € 990,00 € 1.320,00 € 8.070,00 € Figura 6.3: Coste desglosado por secciones y total. Cabe aclarar que las horas de documentación están valoradas como horas de analistas, al igual que las de Análisis y Diseño, que al ser consideradas como fases que se llegan a solapar, se han puesto como una misma sección en el coste desglosado por secciones [Figura 6.3]. Las horas de documentación son muchas debido a que la información disponible sobre drivers para Windows es escasa y en su mayoría está en publicaciones de pago y en inglés, por esto a la hora de documentar se ha invertido mucho tiempo en intentar que este proyecto sirva también de guía para el desarrollo de un driver. Además, el coste por hora de los programadores está por encima del habitual, esto es porque la programación de un driver requiere mayor calidad en la programación que una simple aplicación de escritorio. 52 BIBLIOGRAFÍA Parte7 BIBLIOGRAFÍA 53 BIBLIOGRAFÍA 7 BIBLIOGRAFÍA 7.1 Libros [GONZ09] González-Arintero Berziano, Jaime. “Control Intuitivo de Ratón por Force Feedback”. Universidad Pontificia de Comillas, UPCO, 2009. [ORWI07] Orwick, Penny y Smith, Guy.“Developing Drivers with the Windows® Driver Foundation”. Microsoft Press; 1 edition (April 25, 2007). [ONEY02] Oney, Walter. “Programming the Microsoft Windows Driver Model”. Microsoft Press; 2nd edition (December 16, 2002). 7.2 Links [1] http://msdn.Microsoft.com [2] http://zdoom.org [3] http://java.sun.com/ [4] http://java.sun.com/javase/6/docs/technotes/guides/jni/ 54 ANEXOS Parte8 ANEXOS 55 ANEXOS 8 ANEXOS Anexo A Ejemplo de fichero de instalación .inf [Version] Signature = "$Windows NT$" Class = MyDeviceClass ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} Provider = %ProviderName% CatalogFile=MyCatFile.cat ;==================Class section================== [ClassInstall32] Addreg=MyDeviceClassReg [MyDeviceClassReg] HKR,,,0,%ClassName% HKR,,Icon,,-1 ; ========Manufacturer/Models sections========= [Manufacturer] %ProviderName% = MyDevice_WinUSB,NTx86,NTamd64,NTia64 [MyDevice_WinUSB.NTx86] %USB\MyDevice.DeviceDesc%= USB_Install, USB\VID_0547&PID_1002 [MyDevice_WinUSB.NTamd64] %USB\MyDevice.DeviceDesc%= USB_Install, USB\VID_0547&PID_1002 56 ANEXOS [MyDevice_WinUSB.NTia64] %USB\MyDevice.DeviceDesc%= USB_Install,USB\VID_0547&PID_1002 ;===================Installation=================== ;[1] [USB_Install] Include=winusb.inf Needs=WINUSB.NT ;[2] [USB_Install.Services] Include=winusb.inf AddService=WinUSB,0x00000002,WinUSB_ServiceInstall ;[3] [WinUSB_ServiceInstall] DisplayName = %WinUSB_SvcDesc% ServiceType = 1 StartType = 3 ErrorControl = 1 ServiceBinary = %12%\winusb.sys ;[4] [USB_Install.Wdf] KmdfService=WINUSB, WinUsb_Install [WinUSB_Install] KmdfLibraryVersion=1.9 ;[5] 57 ANEXOS [USB_Install.HW] AddReg=Dev_AddReg [Dev_AddReg] HKR,,DeviceInterfaceGUIDs,0x10000,"{AE5F20A3-09D7-4E62B2F7-3D5172853709}" ;[6] [USB_Install.CoInstallers] AddReg=CoInstallers_AddReg CopyFiles=CoInstallers_CopyFiles [CoInstallers_AddReg] HKR,,CoInstallers32,0x00010000,"WdfCoInstaller01009.dll ,WdfCoInstaller","WinUSBCoInstaller2.dll" [CoInstallers_CopyFiles] WinUSBCoInstaller2.dll WdfCoInstaller01009.dll [DestinationDirs] CoInstallers_CopyFiles=11 ;=================SourceMediaSection=============== ;[7] [SourceDisksNames] 1 = %DISK_NAME%,,,\i386 2 = %DISK_NAME%,,,\amd64 3 = %DISK_NAME%,,,\ia64 [SourceDisksFiles.x86] 58 ANEXOS WinUSBCoInstaller2.dll=1 WdfCoInstaller01009.dll=1 [SourceDisksFiles.NTamd64] WinUSBCoInstaller2.dll=2 WdfCoInstaller01009.dll=2 [SourceDisksFiles.ia64] WinUSBCoInstaller2.dll=3 WdfCoInstaller01009.dll=3 ; =================== Strings =================== [Strings] ProviderName="MyWinUsbTest" USB\MyDevice.DeviceDesc="Test using WinUSB only" WinUSB_SvcDesc="WinUSB Test" DISK_NAME="My Install Disk" ClassName="MyDeviceClass" Anexo B DriverCall.c #include <jni.h> #include "DriverCall.h" #include "E:\windows\VisualC++\VC\include\stdio.h" //#include "SAL.h" // Include Windows headers #include "E:\WinDDK\7600.16385.0\inc\api\windows.h" #include <tchar.h> #include "E:\WinDDK\7600.16385.0\inc\api\strsafe.h" // Include WinUSB headers #include <winusb.h> 59 ANEXOS #include "E:\WinDDK\7600.16385.0\inc\api\Usb100.h" #include "E:\WinDDK\7600.16385.0\inc\api\Setupapi.h" // Linked libraries #pragma comment (lib , "E:\WinDDK\7600.16385.0\lib\wxp\i386\setupapi.lib" ) #pragma comment (lib , "E:\WinDDK\7600.16385.0\lib\wlh\i386\winusb.lib" ) //Estructura del pipe struct PIPE_ID { UCHAR PipeInId; UCHAR PipeOutId; }; // GUID tiene que ser igual que el del fichero.inf static const GUID OSR_DEVICE_INTERFACE = { 0xae5f20a3, 0x09d7, 0x4e62, {0xb2, 0xf7, 0x3d, 0x51, 0x72, 0x85, 0x37, 0x09}}; JNIEXPORT void JNICALL Java_DriverCall_driverCall (JNIEnv *je, jobject jo, jint i) { //printf("Aquí debería ir un write o fwrite al driver guayser\n"); GUID guidDeviceInterface = OSR_DEVICE_INTERFACE; BOOL bResult = TRUE; PIPE_ID PipeID; HANDLE hDeviceHandle = INVALID_HANDLE_VALUE; WINUSB_INTERFACE_HANDLE hWinUSBHandle = INVALID_HANDLE_VALUE; 60 ANEXOS //UCHAR DeviceSpeed; ULONG cbSize = 0; //char que se envía char envio = 0; //convierto i a char switch(i) { case 1: envio = 1; break; case 2: envio = 2; break; case 3: envio = 3; break; case 4: envio = 4; break; case 5: envio = 5; break; case 6: envio = 6; break; case 7: envio = 7; break; case 8: envio = 8; break; case 9: envio = 9; break; case 10: envio = 10; break; case 11: envio = 11; break; case 12: envio = 12; 61 ANEXOS break; case 13: envio = 13; break; case 14: envio = 14; break; case 15: envio = 15; break; case 0: envio = 0; break; default: envio = 0; break; } //Llamada para conseguir un handle para pasarlo en la siguiente función GetDeviceHandle (guidDeviceInterface, &hDeviceHandle); if(!bResult) { goto done; } //Llamada para crear fichero que controle winusb bResult = GetWinUSBHandle(hDeviceHandle, &hWinUSBHandle); if(!bResult) { goto done; } //Llamo para crear pipe con el dispositivo 62 ANEXOS bResult = QueryDeviceEndpoints(hWinUSBHandle, &PipeID); if(!bResult) { goto done; } //Escribo en el driver WriteToBulkEndpoint(hDeviceHandle,&pID,&cbSize, envio); if(!bResult) { goto done; } done: CloseHandle(hDeviceHandle); WinUsb_Free(hWinUSBHandle); } /* CREA FICHERO PARA MANEJAR WinUSB DRIVER para Rigodón */ BOOL GetDeviceHandle (GUID guidDeviceInterface, PHANDLE hDeviceHandle) { if (guidDeviceInterface==GUID_NULL) { return FALSE; } BOOL bResult = TRUE; HDEVINFO hDeviceInfo; SP_DEVINFO_DATA DeviceInfoData; SP_DEVICE_INTERFACE_DATA deviceInterfaceData; PSP_DEVICE_INTERFACE_DETAIL_DATA pInterfaceDetailData = NULL; ULONG requiredLength=0; 63 ANEXOS LPTSTR lpDevicePath = NULL; DWORD index = 0; // Get information about all the installed devices for the specified // device interface class. hDeviceInfo = SetupDiGetClassDevs(&guidDeviceInterface,NULL,NULL,DIGC F_PRESENT|DIGCF_DEVICEINTERFACE); if (hDeviceInfo == INVALID_HANDLE_VALUE) { // ERROR printf("Error SetupDiGetClassDevs: %d.\n", GetLastError()); goto done; } //Enumerate all the device interfaces in the device information set. DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); for (index = 0; SetupDiEnumDeviceInfo(hDeviceInfo, index, &DeviceInfoData); index++) { //Reset for this iteration if (lpDevicePath) { LocalFree(lpDevicePath); } 64 ANEXOS if (pInterfaceDetailData) { LocalFree(pInterfaceDetailData); } deviceInterfaceData.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA); //Get information about the device interface. bResult = SetupDiEnumDeviceInterfaces(hDeviceInfo,&DeviceInfoData ,&guidDeviceInterface,index,&deviceInterfaceData); // Check if last item if (GetLastError () == ERROR_NO_MORE_ITEMS) { break; } //Check for some other error if (!bResult) { printf("Error SetupDiEnumDeviceInterfaces: %d.\n", GetLastError()); goto done; } //Interface data is returned in SP_DEVICE_INTERFACE_DETAIL_DATA //which we need to allocate, so we have to call this function twice. //First to get the size so that we know how 65 ANEXOS much to allocate //Second, the actual call with the allocated buffer bResult = SetupDiGetDeviceInterfaceDetail(hDeviceInfo,&deviceInte rfaceData,NULL, 0,&requiredLength,NULL); //Check for some other error if (!bResult) { if ((ERROR_INSUFFICIENT_BUFFER==GetLastError()) && (requiredLength>0)) { //we got the size, allocate buffer pInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR, requiredLength); if (!pInterfaceDetailData) { // ERROR printf("Error allocating memory for the device detail buffer.\n"); goto done; } } else { printf("Error SetupDiEnumDeviceInterfaces: %d.\n", GetLastError()); goto done; } 66 ANEXOS } //get the interface detailed data pInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); //Now call it with the correct size and allocated buffer bResult = SetupDiGetDeviceInterfaceDetail(hDeviceInfo,&deviceInte rfaceData,pInterfaceDetailData,requiredLength, NULL, &DeviceInfoData); //Check for some other error if (!bResult) { printf("Error SetupDiGetDeviceInterfaceDetail: %d.\n", GetLastError()); goto done; } //copy device path size_t nLength = wcslen (pInterfaceDetailData>DevicePath) + 1; lpDevicePath = (TCHAR *) LocalAlloc (LPTR, nLength * sizeof(TCHAR)); StringCchCopy(lpDevicePath, nLength, pInterfaceDetailData->DevicePath); lpDevicePath[nLength-1] = 0; 67 ANEXOS printf("Device path: %s\n", lpDevicePath); } if (!lpDevicePath) { //Error. printf("Error %d.", GetLastError()); goto done; } //Open the device *hDeviceHandle = CreateFile (lpDevicePath,GENERIC_WRITE,FILE_SHARE_WRITE,NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (*hDeviceHandle == INVALID_HANDLE_VALUE) { //Error. printf("Error %d.", GetLastError()); goto done; } done: LocalFree(lpDevicePath); LocalFree(pInterfaceDetailData); bResult = SetupDiDestroyDeviceInfoList(hDeviceInfo); return bResult; } /* PARA MANEJAR WIN USB, LLAMA A GETDEVICEHANDLE PARA ELLO 68 ANEXOS */ BOOL GetWinUSBHandle(HANDLE hDeviceHandle, PWINUSB_INTERFACE_HANDLE phWinUSBHandle) { if (hDeviceHandle == INVALID_HANDLE_VALUE) { return FALSE; } BOOL bResult = WinUsb_Initialize(hDeviceHandle, phWinUSBHandle); if(!bResult) { //Error. printf("WinUsb_Initialize Error %d.", GetLastError()); return FALSE; } return bResult; } /* Encola los descriptores */ BOOL QueryDeviceEndpoints (WINUSB_INTERFACE_HANDLE hDeviceHandle, PIPE_ID* pipeid) { if (hDeviceHandle==INVALID_HANDLE_VALUE) { return FALSE; } BOOL bResult = TRUE; USB_INTERFACE_DESCRIPTOR InterfaceDescriptor; 69 ANEXOS ZeroMemory(&InterfaceDescriptor, sizeof(USB_INTERFACE_DESCRIPTOR)); WINUSB_PIPE_INFORMATION Pipe; ZeroMemory(&Pipe, sizeof(WINUSB_PIPE_INFORMATION)); bResult = WinUsb_QueryInterfaceSettings(hDeviceHandle, 0, &InterfaceDescriptor); if (bResult) { for (int index = 0; index < InterfaceDescriptor.bNumEndpoints; index++) { bResult = WinUsb_QueryPipe(hDeviceHandle, 0, index, &Pipe); if (bResult) { if (Pipe.PipeType == UsbdPipeTypeControl) { printf("Endpoint index: %d Pipe type: Control Pipe ID: %d.\n", index, Pipe.PipeType, Pipe.PipeId); } if (Pipe.PipeType == UsbdPipeTypeIsochronous) { printf("Endpoint index: %d Pipe type: Isochronous Pipe ID: %d.\n", index, Pipe.PipeType, Pipe.PipeId); } if (Pipe.PipeType == UsbdPipeTypeBulk) 70 ANEXOS { if (USB_ENDPOINT_DIRECTION_IN(Pipe.PipeId)) { printf("Endpoint index: %d Pipe type: Bulk Pipe ID: %c.\n", index, Pipe.PipeType, Pipe.PipeId); pipeid->PipeInId = Pipe.PipeId; } if (USB_ENDPOINT_DIRECTION_OUT(Pipe.PipeId)) { printf("Endpoint index: %d Pipe type: Bulk Pipe ID: %c.\n", index, Pipe.PipeType, Pipe.PipeId); pipeid->PipeOutId = Pipe.PipeId; } } if (Pipe.PipeType == UsbdPipeTypeInterrupt) { printf("Endpoint index: %d Pipe type: Interrupt Pipe ID: %d.\n", index, Pipe.PipeType, Pipe.PipeId); } } else { continue; } } 71 ANEXOS } done: return bResult; } /* ESCRIBE SOBRE EL DRIVER */ BOOL WriteToBulkEndpoint(WINUSB_INTERFACE_HANDLE hDeviceHandle, UCHAR* pID, ULONG* pcbWritten, char envio) { if (hDeviceHandle==INVALID_HANDLE_VALUE || !pID || !pcbWritten) { return FALSE; } BOOL bResult = TRUE; //Tamaño de Buffer de 1 char() UCHAR szBuffer[] = &envio; ULONG cbSize = strlen(szBuffer); ULONG cbSent = 0; bResult = WinUsb_WritePipe(hDeviceHandle, *pID, szBuffer, cbSize, &cbSent, 0); if(!bResult) { goto done; } printf("Wrote to pipe %d: %s \nActual data transferred: %d.\n", *pID, szBuffer, cbSent); *pcbWritten = cbSent; 72 ANEXOS done: return bResult; } Anexo C Manual de instalación de winUSB Para la instalación de WinUSB en su equipo, primero debe coger el paquete de instalación correspondiente a las necesidades de su equipo. Para distiguirlos cada paque tiene un nombre con el formato <Nombre de la distribución de windows><xXX>.rar por ejemplo para la instalación en Windows XP service pack 2 en un equipo x86. El paquete de instalación necesario sería uno con el nombre XPx86.rar. Una vez localizado el paquete lo debe descomprimir en su escritorio. Una vez hecho esto, debe acceder a la carpeta, localizar el fichero install.inf y situar el cursor sobre él. Pulse, con el botón secundario del ratón y pinche en instalar. La instalación debería haberse completando con éxito. 73 ANEXOS Anexo D Manual de la aplicación Para iniciar la ejecución de la aplicación debe tener instalado el Java Runtime Environment, si no es así descárguelo desde http://java.sun.com/. Una vez instalado JRE, solo debe hacer doble click sobre el fichero Rigodon Enforcer.jar para lanzar la aplicación. Con la aplicación abierta tiene dos maneras de comunicarse con el dispositivo, la primera es a partir del slider, sitúese por encima o por debajo de la posición de la marca en la barra y haga click, esto moverá el indicador hacia arriba o hacia abajo respectivamente. Si prefiere realizar un cambio que no sea gradual, escriba un número entre 0 y 15 en el campo de texto y pulse enter. Esto enviará al dispositivo la fuerza introducida en el campo de texto. Para terminar la ejecución pulsar el icono de la “x” en la parte superior izquierda, mediante esta acción el dispositivo pasará a tener fuerza 0 dejando así que pueda trabajar libremente con el resto de aplicaciones. 74 ANEXOS Anexo E Librería rigodon.h #include <stdio.h> //#include "SAL.h" // Include Windows headers #include <windows.h> #include <tchar.h> #include <strsafe.h> // Include WinUSB headers #include <winusb.h> #include <Usb100.h> #include <Setupapi.h> // Linked libraries #pragma comment (lib , "E:\WinDDK\7600.16385.0\lib\wxp\i386\setupapi.lib" ) #pragma comment (lib , "E:\WinDDK\7600.16385.0\lib\wlh\i386\winusb.lib" ) 75 ANEXOS //Estructura del pipe struct PIPE_ID { UCHAR PipeInId; UCHAR PipeOutId; }; // GUID tiene que ser igual que el del fichero.inf static const GUID OSR_DEVICE_INTERFACE = { 0xae5f20a3, 0x09d7, 0x4e62, {0xb2, 0xf7, 0x3d, 0x51, 0x72, 0x85, 0x37, 0x09}}; //Prototipos de función BOOL GetDeviceHandle (GUID, PHANDLE); BOOL GetWinUSBHandle(HANDLE, PWINUSB_INTERFACE_HANDLE); BOOL QueryDeviceEndpoints (WINUSB_INTERFACE_HANDLE, PIPE_ID*); 76 ANEXOS BOOL WriteToBulkEndpoint(WINUSB_INTERFACE_HANDLE, UCHAR*, ULONG*, char); 77