/**************************************************************************** * drivers/virtio/virtio-input.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include "virtio-input.h" #include "virtio-input-event-codes.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define VIRTIO_INPUT_EVENT 0 #define VIRTIO_INPUT_NUM 1 #define VIRTIO_INPUT_EVT_NUM 8 /**************************************************************************** * Private Types ****************************************************************************/ struct virtio_input_priv; typedef void (*virtio_send_event_handler)(FAR struct virtio_input_priv *, FAR struct virtio_input_event *); struct virtio_input_priv { FAR struct virtio_device *vdev; char name[NAME_MAX]; /* Device name */ struct virtio_input_event evt[VIRTIO_INPUT_EVT_NUM]; size_t evtnum; /* Input event number */ struct work_s work; /* Supports the interrupt handling "bottom half" */ virtio_send_event_handler eventhandler; union { struct mouse_lowerhalf_s mouselower; /* Mouse device lowerhalf instance */ struct keyboard_lowerhalf_s keyboardlower; /* Keyboard device lowerhalf instance */ struct touch_lowerhalf_s touchlower; /* Touchpad device lowerhalf instance */ }; union { struct mouse_report_s mousesample; /* Mouse event */ struct keyboard_event_s keyboardsample; /* Keyboard event */ struct touch_sample_s touchsample; /* Touchpad event */ }; }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static int virtio_input_probe(FAR struct virtio_device *vdev); static void virtio_input_remove(FAR struct virtio_device *vdev); /**************************************************************************** * Private Data ****************************************************************************/ static struct virtio_driver g_virtio_input_driver = { .node = LIST_INITIAL_VALUE(g_virtio_input_driver.node), /* node */ .device = VIRTIO_ID_INPUT, /* device id */ .probe = virtio_input_probe, /* probe */ .remove = virtio_input_remove, /* remove */ }; static int g_virtio_mouse_idx = 0; static int g_virtio_touch_idx = 0; static int g_virtio_keyboard_idx = 0; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: virtio_input_translate_keycode ****************************************************************************/ static uint32_t virtio_input_translate_keycode(uint16_t keycode) { switch (keycode) { case KEY_DELETE: return KEYCODE_FWDDEL; case KEY_BACKSPACE: return KEYCODE_BACKDEL; case KEY_HOME: return KEYCODE_HOME; case KEY_END: return KEYCODE_END; case KEY_LEFT: return KEYCODE_LEFT; case KEY_RIGHT: return KEYCODE_RIGHT; case KEY_UP: return KEYCODE_UP; case KEY_DOWN: return KEYCODE_DOWN; case KEY_PAGEUP: return KEYCODE_PAGEUP; case KEY_PAGEDOWN: return KEYCODE_PAGEDOWN; case KEY_ENTER: return KEYCODE_ENTER; case KEY_CAPSLOCK: return KEYCODE_CAPSLOCK; case KEY_SCROLLLOCK: return KEYCODE_SCROLLLOCK; case KEY_NUMLOCK: return KEYCODE_NUMLOCK; case KEY_SYSRQ: return KEYCODE_PRTSCRN; case KEY_F1: return KEYCODE_F1; case KEY_F2: return KEYCODE_F2; case KEY_F3: return KEYCODE_F3; case KEY_F4: return KEYCODE_F4; case KEY_F5: return KEYCODE_F5; case KEY_F6: return KEYCODE_F6; case KEY_F7: return KEYCODE_F7; case KEY_F8: return KEYCODE_F8; case KEY_F9: return KEYCODE_F9; case KEY_F10: return KEYCODE_F10; case KEY_F11: return KEYCODE_F11; case KEY_F12: return KEYCODE_F12; case KEY_F13: return KEYCODE_F13; case KEY_F14: return KEYCODE_F14; case KEY_F15: return KEYCODE_F15; case KEY_F16: return KEYCODE_F16; case KEY_F17: return KEYCODE_F17; case KEY_F18: return KEYCODE_F18; case KEY_F19: return KEYCODE_F19; case KEY_F20: return KEYCODE_F20; case KEY_F21: return KEYCODE_F21; case KEY_F22: return KEYCODE_F22; case KEY_F23: return KEYCODE_F23; case KEY_F24: return KEYCODE_F24; default: return keycode; } } /**************************************************************************** * Name: virtio_input_send_keyboard_event ****************************************************************************/ static void virtio_input_send_keyboard_event(FAR struct virtio_input_priv *priv, FAR struct virtio_input_event *event) { if (event->type == EV_KEY) { priv->keyboardsample.code = virtio_input_translate_keycode(event->code); priv->keyboardsample.type = event->value; } else if (event->type == EV_SYN && event->code == SYN_REPORT) { keyboard_event(&(priv->keyboardlower), priv->keyboardsample.code, priv->keyboardsample.type); memset(&priv->keyboardsample, 0, sizeof(priv->keyboardsample)); } } /**************************************************************************** * Name: virtio_input_send_mouse_event ****************************************************************************/ static void virtio_input_send_mouse_event(FAR struct virtio_input_priv *priv, FAR struct virtio_input_event *event) { if (event->type == EV_REL) { switch (event->code) { case REL_X: priv->mousesample.x = event->value; break; case REL_Y: priv->mousesample.y = event->value; break; #ifdef CONFIG_INPUT_MOUSE_WHEEL case REL_WHEEL: priv->mousesample.wheel = event->value; break; #endif } } else if (event->type == EV_KEY) { switch (event->code) { case BTN_LEFT: if (event->value) { priv->mousesample.buttons |= MOUSE_BUTTON_1; } break; case BTN_RIGHT: if (event->value) { priv->mousesample.buttons |= MOUSE_BUTTON_2; } break; case BTN_MIDDLE: if (event->value) { priv->mousesample.buttons |= MOUSE_BUTTON_3; } break; } } else if (event->type == EV_SYN && event->code == SYN_REPORT) { mouse_event(priv->mouselower.priv, &priv->mousesample); memset(&priv->mousesample, 0, sizeof(priv->mousesample)); } } /**************************************************************************** * Name: virtio_input_send_touch_event ****************************************************************************/ static void virtio_input_send_touch_event(FAR struct virtio_input_priv *priv, FAR struct virtio_input_event *event) { if (event->type == EV_ABS) { switch (event->code) { case ABS_PRESSURE: priv->touchsample.point[0].flags |= TOUCH_PRESSURE_VALID; priv->touchsample.point[0].pressure = event->value; break; case ABS_X: priv->touchsample.point[0].flags |= TOUCH_POS_VALID; priv->touchsample.point[0].x = event->value; break; case ABS_Y: priv->touchsample.point[0].flags |= TOUCH_POS_VALID; priv->touchsample.point[0].y = event->value; break; } } else if (event->type == EV_SYN && event->code == SYN_REPORT) { priv->touchsample.npoints = 1; priv->touchsample.point[0].timestamp = touch_get_time(); touch_event(priv->touchlower.priv, &priv->touchsample); memset(&priv->touchsample, 0, sizeof(priv->touchsample)); } } /**************************************************************************** * Name: virtio_input_worker ****************************************************************************/ static void virtio_input_worker(FAR void *arg) { FAR struct virtio_input_priv *priv = (FAR struct virtio_input_priv *)arg; FAR struct virtqueue *vq = priv->vdev->vrings_info[VIRTIO_INPUT_EVENT].vq; FAR struct virtio_input_event *evt; FAR struct virtqueue_buf vb; uint32_t len; while ((evt = (FAR struct virtio_input_event *) virtqueue_get_buffer(vq, &len, NULL)) != NULL) { vrtinfo("virtio_input_worker (type,code,value) - (%d,%d,%d).\n", evt->type, evt->code, evt->value); priv->eventhandler(priv, evt); vb.buf = evt; vb.len = len; virtqueue_add_buffer(vq, &vb, 0, 1, vb.buf); } virtqueue_kick(vq); } /**************************************************************************** * Name: virtio_input_recv_events ****************************************************************************/ static void virtio_input_recv_events(FAR struct virtqueue *vq) { FAR struct virtio_input_priv *priv = vq->vq_dev->priv; int ret; ret = work_queue(HPWORK, &priv->work, virtio_input_worker, priv, 0); if (ret != 0) { vrterr("ERROR: Failed to queue work: %d\n", ret); } return; } /**************************************************************************** * Name: virtio_input_fill_event ****************************************************************************/ static void virtio_input_fill_event(FAR struct virtio_input_priv *priv) { FAR struct virtqueue *vq = priv->vdev->vrings_info[VIRTIO_INPUT_EVENT].vq; FAR struct virtqueue_buf vb; int i; for (i = 0; i < priv->evtnum; i++) { vb.buf = &priv->evt[i]; vb.len = sizeof(struct virtio_input_event); virtqueue_add_buffer(vq, &vb, 0, 1, vb.buf); } virtqueue_kick(vq); } /**************************************************************************** * Name: virtio_input_select_cfg ****************************************************************************/ static uint8_t virtio_input_select_cfg(FAR struct virtio_input_priv *priv, uint8_t select, uint8_t subsel) { uint8_t cfg_size = 0; virtio_write_config(priv->vdev, offsetof(struct virtio_input_config, select), &select, sizeof(uint8_t)); virtio_write_config(priv->vdev, offsetof(struct virtio_input_config, subsel), &subsel, sizeof(uint8_t)); virtio_read_config(priv->vdev, offsetof(struct virtio_input_config, size), &cfg_size, sizeof(uint8_t)); return cfg_size; } /**************************************************************************** * Name: virtio_input_register ****************************************************************************/ static void virtio_input_register(FAR struct virtio_input_priv *priv) { if (virtio_input_select_cfg(priv, VIRTIO_INPUT_CFG_EV_BITS, EV_ABS)) { priv->touchlower.maxpoint = 1; snprintf(priv->name, NAME_MAX, "/dev/virtinput%d", g_virtio_touch_idx++); touch_register(&(priv->touchlower), priv->name, priv->evtnum); priv->eventhandler = virtio_input_send_touch_event; } else if (virtio_input_select_cfg(priv, VIRTIO_INPUT_CFG_EV_BITS, EV_REL)) { snprintf(priv->name, NAME_MAX, "/dev/virtmouse%d", g_virtio_mouse_idx++); mouse_register(&(priv->mouselower), priv->name, priv->evtnum); priv->eventhandler = virtio_input_send_mouse_event; } else if (virtio_input_select_cfg(priv, VIRTIO_INPUT_CFG_EV_BITS, EV_KEY)) { snprintf(priv->name, NAME_MAX, "/dev/virtkbd%d", g_virtio_keyboard_idx++); keyboard_register(&(priv->keyboardlower), priv->name, priv->evtnum); priv->eventhandler = virtio_input_send_keyboard_event; } } /**************************************************************************** * Name: virtio_input_probe ****************************************************************************/ static int virtio_input_probe(FAR struct virtio_device *vdev) { FAR struct virtio_input_priv *priv; FAR const char *vqnames[VIRTIO_INPUT_NUM]; vq_callback callbacks[VIRTIO_INPUT_NUM]; int ret; priv = kmm_zalloc(sizeof(*priv)); if (priv == NULL) { vrterr("No enough memory\n"); return -ENOMEM; } priv->vdev = vdev; vdev->priv = priv; /* Initialize the virtio device */ virtio_set_status(vdev, VIRTIO_CONFIG_STATUS_DRIVER); virtio_set_features(vdev, 0); virtio_set_status(vdev, VIRTIO_CONFIG_FEATURES_OK); vqnames[VIRTIO_INPUT_EVENT] = "virtio_input_event"; callbacks[VIRTIO_INPUT_EVENT] = virtio_input_recv_events; ret = virtio_create_virtqueues(vdev, 0, VIRTIO_INPUT_NUM, vqnames, callbacks); if (ret < 0) { vrterr("virtio_device_create_virtqueue failed, ret=%d\n", ret); virtio_reset_device(vdev); kmm_free(priv); return ret; } virtio_set_status(vdev, VIRTIO_CONFIG_STATUS_DRIVER_OK); virtqueue_enable_cb(vdev->vrings_info[VIRTIO_INPUT_EVENT].vq); priv->evtnum = MIN(vdev->vrings_info[VIRTIO_INPUT_EVENT].info.num_descs, VIRTIO_INPUT_EVT_NUM); /* register lower half drivers */ virtio_input_register(priv); virtio_input_fill_event(priv); return ret; } /**************************************************************************** * Name: virtio_input_remove ****************************************************************************/ static void virtio_input_remove(FAR struct virtio_device *vdev) { FAR struct virtio_input_priv *priv = vdev->priv; if (priv->eventhandler == virtio_input_send_keyboard_event) { keyboard_unregister(&(priv->keyboardlower), priv->name); } else if (priv->eventhandler == virtio_input_send_mouse_event) { mouse_unregister(&(priv->mouselower), priv->name); } else if (priv->eventhandler == virtio_input_send_touch_event) { touch_unregister(&(priv->touchlower), priv->name); } virtio_reset_device(vdev); virtio_delete_virtqueues(vdev); kmm_free(priv); } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: virtio_register_input_driver ****************************************************************************/ int virtio_register_input_driver(void) { return virtio_register_driver(&g_virtio_input_driver); }