mousedev.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2011 Lubos Slovak, Vojtech Horky
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027  */
00028 
00037 #include <usb/debug.h>
00038 #include <usb/classes/classes.h>
00039 #include <usb/hid/hid.h>
00040 #include <usb/hid/request.h>
00041 #include <usb/hid/usages/core.h>
00042 #include <errno.h>
00043 #include <str_error.h>
00044 #include <ipc/mouse.h>
00045 #include <io/console.h>
00046 
00047 #include <ipc/kbd.h>
00048 #include <io/keycode.h>
00049 
00050 #include "mousedev.h"
00051 #include "../usbhid.h"
00052 
00053 #define NAME "mouse"
00054 
00055 /*----------------------------------------------------------------------------*/
00056 
00057 usb_endpoint_description_t usb_hid_mouse_poll_endpoint_description = {
00058         .transfer_type = USB_TRANSFER_INTERRUPT,
00059         .direction = USB_DIRECTION_IN,
00060         .interface_class = USB_CLASS_HID,
00061         .interface_subclass = USB_HID_SUBCLASS_BOOT,
00062         .interface_protocol = USB_HID_PROTOCOL_MOUSE,
00063         .flags = 0
00064 };
00065 
00066 const char *HID_MOUSE_FUN_NAME = "mouse";
00067 const char *HID_MOUSE_WHEEL_FUN_NAME = "mouse-wheel";
00068 const char *HID_MOUSE_CLASS_NAME = "mouse";
00069 const char *HID_MOUSE_WHEEL_CLASS_NAME = "keyboard";
00070 
00072 static const uint8_t IDLE_RATE = 0;
00073 static const size_t USB_MOUSE_BUTTON_COUNT = 3;
00074 
00075 /*----------------------------------------------------------------------------*/
00076 
00077 enum {
00078         USB_MOUSE_BOOT_REPORT_DESCRIPTOR_SIZE = 63
00079 };
00080 
00081 static const uint8_t USB_MOUSE_BOOT_REPORT_DESCRIPTOR[
00082     USB_MOUSE_BOOT_REPORT_DESCRIPTOR_SIZE] = {
00083         0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
00084         0x09, 0x02,                    // USAGE (Mouse)
00085         0xa1, 0x01,                    // COLLECTION (Application)
00086         0x09, 0x01,                    //   USAGE (Pointer)
00087         0xa1, 0x00,                    //   COLLECTION (Physical)
00088         0x95, 0x03,                    //     REPORT_COUNT (3)
00089         0x75, 0x01,                    //     REPORT_SIZE (1)
00090         0x05, 0x09,                    //     USAGE_PAGE (Button)
00091         0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
00092         0x29, 0x03,                    //     USAGE_MAXIMUM (Button 3)
00093         0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
00094         0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
00095         0x81, 0x02,                    //     INPUT (Data,Var,Abs)
00096         0x95, 0x01,                    //     REPORT_COUNT (1)
00097         0x75, 0x05,                    //     REPORT_SIZE (5)
00098         0x81, 0x01,                    //     INPUT (Cnst)
00099         0x75, 0x08,                    //     REPORT_SIZE (8)
00100         0x95, 0x02,                    //     REPORT_COUNT (2)
00101         0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
00102         0x09, 0x30,                    //     USAGE (X)
00103         0x09, 0x31,                    //     USAGE (Y)
00104         0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)
00105         0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)
00106         0x81, 0x06,                    //     INPUT (Data,Var,Rel)
00107         0xc0,                          //   END_COLLECTION
00108         0xc0                           // END_COLLECTION
00109 };
00110 
00111 /*----------------------------------------------------------------------------*/
00112 
00119 static void default_connection_handler(ddf_fun_t *fun,
00120     ipc_callid_t icallid, ipc_call_t *icall)
00121 {
00122         sysarg_t method = IPC_GET_IMETHOD(*icall);
00123         
00124         usb_mouse_t *mouse_dev = (usb_mouse_t *)fun->driver_data;
00125         
00126         if (mouse_dev == NULL) {
00127                 usb_log_debug("default_connection_handler: Missing "
00128                     "parameters.\n");
00129                 async_answer_0(icallid, EINVAL);
00130                 return;
00131         }
00132         
00133         usb_log_debug("default_connection_handler: fun->name: %s\n",
00134                       fun->name);
00135         usb_log_debug("default_connection_handler: mouse_phone: %d, wheel "
00136             "phone: %d\n", mouse_dev->mouse_phone, mouse_dev->wheel_phone);
00137         
00138         int *phone = (str_cmp(fun->name, HID_MOUSE_FUN_NAME) == 0) 
00139                      ? &mouse_dev->mouse_phone : &mouse_dev->wheel_phone;
00140         
00141         if (method == IPC_M_CONNECT_TO_ME) {
00142                 int callback = IPC_GET_ARG5(*icall);
00143 
00144                 if (*phone != -1) {
00145                         usb_log_debug("default_connection_handler: Console "
00146                             "phone to mouse already set.\n");
00147                         async_answer_0(icallid, ELIMIT);
00148                         return;
00149                 }
00150 
00151                 *phone = callback;
00152                 usb_log_debug("Console phone to mouse set ok (%d).\n", *phone);
00153                 async_answer_0(icallid, EOK);
00154                 return;
00155         }
00156 
00157         usb_log_debug("default_connection_handler: Invalid function.\n");
00158         async_answer_0(icallid, EINVAL);
00159 }
00160 
00161 /*----------------------------------------------------------------------------*/
00162 
00163 static usb_mouse_t *usb_mouse_new(void)
00164 {
00165         usb_mouse_t *mouse = calloc(1, sizeof(usb_mouse_t));
00166         if (mouse == NULL) {
00167                 return NULL;
00168         }
00169         mouse->mouse_phone = -1;
00170         mouse->wheel_phone = -1;
00171         
00172         return mouse;
00173 }
00174 
00175 /*----------------------------------------------------------------------------*/
00176 
00177 static void usb_mouse_free(usb_mouse_t **mouse_dev)
00178 {
00179         assert(mouse_dev != NULL && *mouse_dev != NULL);
00180         
00181         // hangup phone to the console
00182         if ((*mouse_dev)->mouse_phone >= 0) {
00183                 async_hangup((*mouse_dev)->mouse_phone);
00184         }
00185         
00186         if ((*mouse_dev)->wheel_phone >= 0) {
00187                 async_hangup((*mouse_dev)->wheel_phone);
00188         }
00189         
00190         free(*mouse_dev);
00191         *mouse_dev = NULL;
00192 }
00193 
00194 /*----------------------------------------------------------------------------*/
00195 
00196 static void usb_mouse_send_wheel(const usb_mouse_t *mouse_dev, int wheel)
00197 {
00198         console_event_t ev;
00199         
00200         ev.type = KEY_PRESS;
00201         ev.key = (wheel > 0) ? KC_UP : (wheel < 0) ? KC_DOWN : 0;
00202         ev.mods = 0;
00203         ev.c = 0;
00204 
00205         if (mouse_dev->wheel_phone < 0) {
00206                 usb_log_warning(
00207                     "Connection to console not ready, key discarded.\n");
00208                 return;
00209         }
00210         
00211         int count = (wheel < 0) ? -wheel : wheel;
00212         int i;
00213         
00214         for (i = 0; i < count * 3; ++i) {
00215                 usb_log_debug2("Sending key %d to the console\n", ev.key);
00216                 async_msg_4(mouse_dev->wheel_phone, KBD_EVENT, ev.type, 
00217                     ev.key, ev.mods, ev.c);
00218                 // send key release right away
00219                 async_msg_4(mouse_dev->wheel_phone, KBD_EVENT, KEY_RELEASE, 
00220                     ev.key, ev.mods, ev.c);
00221         }
00222 }
00223 
00224 /*----------------------------------------------------------------------------*/
00225 
00226 static bool usb_mouse_process_report(usb_hid_dev_t *hid_dev, 
00227                                      usb_mouse_t *mouse_dev/*, uint8_t *buffer,
00228                                      size_t buffer_size*/)
00229 {
00230         assert(mouse_dev != NULL);
00231         
00232 //      usb_log_debug2("got buffer: %s.\n",
00233 //          usb_debug_str_buffer(buffer, buffer_size, 0));
00234         
00235         if (mouse_dev->mouse_phone < 0) {
00236                 usb_log_warning(NAME " No console phone.\n");
00237                 return true;
00238         }
00239 
00240         /*
00241          * parse the input report
00242          */
00243         
00244 //      usb_log_debug(NAME " Calling usb_hid_parse_report() with "
00245 //          "buffer %s\n", usb_debug_str_buffer(buffer, buffer_size, 0));
00246         
00247 //      uint8_t report_id;
00248         
00249 //      int rc = usb_hid_parse_report(hid_dev->report, buffer, buffer_size, 
00250 //          &report_id);
00251         
00252 //      if (rc != EOK) {
00253 //              usb_log_warning(NAME "Error in usb_hid_parse_report(): %s\n", 
00254 //                  str_error(rc));
00255 //              return true;
00256 //      }
00257         
00258         /*
00259          * X
00260          */
00261         int shift_x = 0;
00262         
00263         usb_hid_report_path_t *path = usb_hid_report_path();
00264         usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_GENERIC_DESKTOP, 
00265             USB_HIDUT_USAGE_GENERIC_DESKTOP_X);
00266 
00267         usb_hid_report_path_set_report_id(path, hid_dev->report_id);
00268 
00269         usb_hid_report_field_t *field = usb_hid_report_get_sibling(
00270             hid_dev->report, NULL, path, USB_HID_PATH_COMPARE_END, 
00271             USB_HID_REPORT_TYPE_INPUT);
00272 
00273         if (field != NULL) {
00274                 usb_log_debug(NAME " VALUE(%X) USAGE(%X)\n", field->value, 
00275                     field->usage);
00276                 shift_x = field->value;
00277         }
00278 
00279         usb_hid_report_path_free(path);
00280         
00281         /*
00282          * Y
00283          */
00284         int shift_y = 0;
00285         
00286         path = usb_hid_report_path();
00287         usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_GENERIC_DESKTOP, 
00288             USB_HIDUT_USAGE_GENERIC_DESKTOP_Y);
00289 
00290         usb_hid_report_path_set_report_id(path, hid_dev->report_id);
00291 
00292         field = usb_hid_report_get_sibling(
00293             hid_dev->report, NULL, path, USB_HID_PATH_COMPARE_END, 
00294             USB_HID_REPORT_TYPE_INPUT);
00295 
00296         if (field != NULL) {
00297                 usb_log_debug(NAME " VALUE(%X) USAGE(%X)\n", field->value, 
00298                     field->usage);
00299                 shift_y = field->value;
00300         }
00301 
00302         usb_hid_report_path_free(path);
00303         
00304         if ((shift_x != 0) || (shift_y != 0)) {
00305                 async_req_2_0(mouse_dev->mouse_phone,
00306                     MEVENT_MOVE, shift_x, shift_y);
00307         }
00308         
00309         /*
00310          * Wheel
00311          */
00312         int wheel = 0;
00313         
00314         path = usb_hid_report_path();
00315         usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_GENERIC_DESKTOP, 
00316             USB_HIDUT_USAGE_GENERIC_DESKTOP_WHEEL);
00317 
00318         usb_hid_report_path_set_report_id(path, hid_dev->report_id);
00319         
00320         field = usb_hid_report_get_sibling(
00321             hid_dev->report, NULL, path, USB_HID_PATH_COMPARE_END, 
00322             USB_HID_REPORT_TYPE_INPUT);
00323 
00324         if (field != NULL) {
00325                 usb_log_debug(NAME " VALUE(%X) USAGE(%X)\n", field->value, 
00326                     field->usage);
00327                 wheel = field->value;
00328         }
00329 
00330         usb_hid_report_path_free(path);
00331         
00332         // send arrow up for positive direction and arrow down for negative
00333         // direction; three arrows for difference of 1
00334         usb_mouse_send_wheel(mouse_dev, wheel);
00335         
00336         
00337         /*
00338          * Buttons
00339          */
00340         path = usb_hid_report_path();
00341         usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_BUTTON, 0);
00342         usb_hid_report_path_set_report_id(path, hid_dev->report_id);
00343         
00344         field = usb_hid_report_get_sibling(
00345             hid_dev->report, NULL, path, USB_HID_PATH_COMPARE_END
00346             | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY, 
00347             USB_HID_REPORT_TYPE_INPUT);
00348 
00349         while (field != NULL) {
00350                 usb_log_debug(NAME " VALUE(%X) USAGE(%X)\n", field->value, 
00351                     field->usage);
00352                 
00353                 if (mouse_dev->buttons[field->usage - field->usage_minimum] == 0
00354                     && field->value != 0) {
00355                         async_req_2_0(mouse_dev->mouse_phone,
00356                             MEVENT_BUTTON, field->usage, 1);
00357                         mouse_dev->buttons[field->usage - field->usage_minimum]
00358                             = field->value;
00359                 } else if (
00360                     mouse_dev->buttons[field->usage - field->usage_minimum] != 0
00361                     && field->value == 0) {
00362                        async_req_2_0(mouse_dev->mouse_phone,
00363                            MEVENT_BUTTON, field->usage, 0);
00364                        mouse_dev->buttons[field->usage - field->usage_minimum]
00365                            = field->value;
00366                }
00367                 
00368                 field = usb_hid_report_get_sibling(
00369                     hid_dev->report, field, path, USB_HID_PATH_COMPARE_END
00370                     | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY, 
00371                     USB_HID_REPORT_TYPE_INPUT);
00372         }
00373         
00374         usb_hid_report_path_free(path);
00375 
00376         return true;
00377 }
00378 
00379 /*----------------------------------------------------------------------------*/
00380 
00381 static int usb_mouse_create_function(usb_hid_dev_t *hid_dev, usb_mouse_t *mouse)
00382 {
00383         assert(hid_dev != NULL);
00384         assert(mouse != NULL);
00385         
00386         /* Create the function exposed under /dev/devices. */
00387         usb_log_debug("Creating DDF function %s...\n", HID_MOUSE_FUN_NAME);
00388         ddf_fun_t *fun = ddf_fun_create(hid_dev->usb_dev->ddf_dev, fun_exposed, 
00389             HID_MOUSE_FUN_NAME);
00390         if (fun == NULL) {
00391                 usb_log_error("Could not create DDF function node.\n");
00392                 return ENOMEM;
00393         }
00394         
00395         fun->ops = &mouse->ops;
00396         fun->driver_data = mouse;
00397 
00398         int rc = ddf_fun_bind(fun);
00399         if (rc != EOK) {
00400                 usb_log_error("Could not bind DDF function: %s.\n",
00401                     str_error(rc));
00402                 ddf_fun_destroy(fun);
00403                 return rc;
00404         }
00405         
00406         usb_log_debug("Adding DDF function to class %s...\n", 
00407             HID_MOUSE_CLASS_NAME);
00408         rc = ddf_fun_add_to_class(fun, HID_MOUSE_CLASS_NAME);
00409         if (rc != EOK) {
00410                 usb_log_error(
00411                     "Could not add DDF function to class %s: %s.\n",
00412                     HID_MOUSE_CLASS_NAME, str_error(rc));
00413                 ddf_fun_destroy(fun);
00414                 return rc;
00415         }
00416         
00417         /*
00418          * Special function for acting as keyboard (wheel)
00419          */
00420         usb_log_debug("Creating DDF function %s...\n", 
00421                       HID_MOUSE_WHEEL_FUN_NAME);
00422         fun = ddf_fun_create(hid_dev->usb_dev->ddf_dev, fun_exposed, 
00423             HID_MOUSE_WHEEL_FUN_NAME);
00424         if (fun == NULL) {
00425                 usb_log_error("Could not create DDF function node.\n");
00426                 return ENOMEM;
00427         }
00428         
00429         /*
00430          * Store the initialized HID device and HID ops
00431          * to the DDF function.
00432          */
00433         fun->ops = &mouse->ops;
00434         fun->driver_data = mouse;
00435 
00436         rc = ddf_fun_bind(fun);
00437         if (rc != EOK) {
00438                 usb_log_error("Could not bind DDF function: %s.\n",
00439                     str_error(rc));
00440                 ddf_fun_destroy(fun);
00441                 return rc;
00442         }
00443         
00444         usb_log_debug("Adding DDF function to class %s...\n", 
00445             HID_MOUSE_WHEEL_CLASS_NAME);
00446         rc = ddf_fun_add_to_class(fun, HID_MOUSE_WHEEL_CLASS_NAME);
00447         if (rc != EOK) {
00448                 usb_log_error(
00449                     "Could not add DDF function to class %s: %s.\n",
00450                     HID_MOUSE_WHEEL_CLASS_NAME, str_error(rc));
00451                 ddf_fun_destroy(fun);
00452                 return rc;
00453         }
00454         
00455         return EOK;
00456 }
00457 
00458 /*----------------------------------------------------------------------------*/
00459 
00460 int usb_mouse_init(usb_hid_dev_t *hid_dev, void **data)
00461 {
00462         usb_log_debug("Initializing HID/Mouse structure...\n");
00463         
00464         if (hid_dev == NULL) {
00465                 usb_log_error("Failed to init keyboard structure: no structure"
00466                     " given.\n");
00467                 return EINVAL;
00468         }
00469         
00470         usb_mouse_t *mouse_dev = usb_mouse_new();
00471         if (mouse_dev == NULL) {
00472                 usb_log_error("Error while creating USB/HID Mouse device "
00473                     "structure.\n");
00474                 return ENOMEM;
00475         }
00476         
00477         mouse_dev->buttons = (int32_t *)calloc(USB_MOUSE_BUTTON_COUNT, 
00478             sizeof(int32_t));
00479         
00480         if (mouse_dev->buttons == NULL) {
00481                 usb_log_fatal("No memory!\n");
00482                 free(mouse_dev);
00483                 return ENOMEM;
00484         }
00485         
00486         // save the Mouse device structure into the HID device structure
00487         *data = mouse_dev;
00488         
00489         // set handler for incoming calls
00490         mouse_dev->ops.default_handler = default_connection_handler;
00491         
00492         // TODO: how to know if the device supports the request???
00493         usbhid_req_set_idle(&hid_dev->usb_dev->ctrl_pipe, 
00494             hid_dev->usb_dev->interface_no, IDLE_RATE);
00495         
00496         int rc = usb_mouse_create_function(hid_dev, mouse_dev);
00497         if (rc != EOK) {
00498                 usb_mouse_free(&mouse_dev);
00499                 return rc;
00500         }
00501         
00502         return EOK;
00503 }
00504 
00505 /*----------------------------------------------------------------------------*/
00506 
00507 bool usb_mouse_polling_callback(usb_hid_dev_t *hid_dev, void *data/*, 
00508      uint8_t *buffer, size_t buffer_size*/)
00509 {
00510         usb_log_debug("usb_mouse_polling_callback()\n");
00511 //      usb_debug_str_buffer(buffer, buffer_size, 0);
00512         
00513         if (hid_dev == NULL || data == NULL) {
00514                 usb_log_error("Missing argument to the mouse polling callback."
00515                     "\n");
00516                 return false;
00517         }
00518         
00519         usb_mouse_t *mouse_dev = (usb_mouse_t *)data;
00520                 
00521         return usb_mouse_process_report(hid_dev, mouse_dev/*, buffer, 
00522                                         buffer_size*/);
00523 }
00524 
00525 /*----------------------------------------------------------------------------*/
00526 
00527 void usb_mouse_deinit(usb_hid_dev_t *hid_dev, void *data)
00528 {
00529         if (data != NULL) {
00530                 usb_mouse_free((usb_mouse_t **)&data);
00531         }
00532 }
00533 
00534 /*----------------------------------------------------------------------------*/
00535 
00536 int usb_mouse_set_boot_protocol(usb_hid_dev_t *hid_dev)
00537 {
00538         int rc = usb_hid_parse_report_descriptor(hid_dev->report, 
00539             USB_MOUSE_BOOT_REPORT_DESCRIPTOR, 
00540             USB_MOUSE_BOOT_REPORT_DESCRIPTOR_SIZE);
00541         
00542         if (rc != EOK) {
00543                 usb_log_error("Failed to parse boot report descriptor: %s\n",
00544                     str_error(rc));
00545                 return rc;
00546         }
00547         
00548         rc = usbhid_req_set_protocol(&hid_dev->usb_dev->ctrl_pipe, 
00549             hid_dev->usb_dev->interface_no, USB_HID_PROTOCOL_BOOT);
00550         
00551         if (rc != EOK) {
00552                 usb_log_warning("Failed to set boot protocol to the device: "
00553                     "%s\n", str_error(rc));
00554                 return rc;
00555         }
00556         
00557         return EOK;
00558 }
00559 

Generated on Thu Jun 2 07:45:44 2011 for HelenOS/USB by  doxygen 1.4.7