xref: /device/qemu/drivers/virtio/virtinput.c (revision d6aed566)
1/*
2 * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "osal.h"
17#include "osal_io.h"
18#include "hdf_input_device_manager.h"
19#include "hdf_hid_adapter.h"
20#include "utils/hdf_workqueue.h"
21#include "los_vm_iomap.h"
22#include "virtmmio.h"
23
24#define VIRTQ_EVENT_QSZ     8
25#define VIRTQ_STATUS_QSZ    1
26#define VIRTMMIO_INPUT_NAME "virtinput"
27
28/*
29 * QEMU virtio-tablet coordinates sit in a fixed square:
30#define INPUT_EVENT_ABS_MIN    0x0000
31#define INPUT_EVENT_ABS_MAX    0x7FFF
32 */
33#define QEMU_TABLET_LEN        0x8000
34
35enum {
36    VIRTIO_INPUT_CFG_UNSET      = 0x00,
37    VIRTIO_INPUT_CFG_ID_NAME    = 0x01,
38    VIRTIO_INPUT_CFG_ID_SERIAL  = 0x02,
39    VIRTIO_INPUT_CFG_ID_DEVIDS  = 0x03,
40    VIRTIO_INPUT_CFG_PROP_BITS  = 0x10,
41    VIRTIO_INPUT_CFG_EV_BITS    = 0x11,
42    VIRTIO_INPUT_CFG_ABS_INFO   = 0x12,
43};
44
45struct VirtinAbsinfo {
46    uint32_t min;
47    uint32_t max;
48    uint32_t fuzz;
49    uint32_t flat;
50    uint32_t res;
51};
52
53struct VirtinDevids {
54    uint16_t bus;
55    uint16_t vendor;
56    uint16_t product;
57    uint16_t version;
58};
59
60struct VirtinConfig {
61    uint8_t select;
62    uint8_t subsel;
63    uint8_t size;
64#define VIRTIN_PADDINGS  5
65    uint8_t reserved[VIRTIN_PADDINGS];
66    union {
67#define VIRTIN_PROP_LEN  128
68        char string[VIRTIN_PROP_LEN];
69        uint8_t bitmap[VIRTIN_PROP_LEN];
70        struct VirtinAbsinfo abs;
71        struct VirtinDevids ids;
72    } u;
73};
74
75struct VirtinEvent {
76    uint16_t type;
77    uint16_t code;
78    uint32_t value;
79};
80
81struct Virtin {
82    struct VirtmmioDev dev;
83
84    struct VirtinEvent ev[VIRTQ_EVENT_QSZ]; /* event receive buffer */
85    HdfWorkQueue wq;                        /* event work-queue */
86};
87static const InputDevice *g_virtInputDev; /* work thread need this data, using global for simplicity */
88
89static bool Feature0(uint32_t features, uint32_t *supported, void *dev)
90{
91    (void)features;
92    (void)supported;
93    (void)dev;
94    return true;
95}
96
97static bool Feature1(uint32_t features, uint32_t *supported, void *dev)
98{
99    (void)dev;
100    if (features & VIRTIO_F_VERSION_1) {
101        *supported |= VIRTIO_F_VERSION_1;
102    } else {
103        HDF_LOGE("[%s]virtio-mmio input has no VERSION_1 feature", __func__);
104        return false;
105    }
106
107    return true;
108}
109
110static void PopulateEventQ(const struct Virtin *in)
111{
112    const struct Virtq *q = &in->dev.vq[0];
113    int i;
114
115    for (i = 0; i < VIRTQ_EVENT_QSZ; i++) {
116        q->desc[i].pAddr = VMM_TO_DMA_ADDR((VADDR_T)&in->ev[i]);
117        q->desc[i].len = sizeof(struct VirtinEvent);
118        q->desc[i].flag = VIRTQ_DESC_F_WRITE;
119
120        q->avail->ring[i] = i;
121    }
122
123    in->dev.vq[0].avail->index += in->dev.vq[0].qsz;
124    OSAL_WRITEL(0, in->dev.base + VIRTMMIO_REG_QUEUENOTIFY);
125}
126
127static void VirtinWorkCallback(void *arg)
128{
129    struct VirtinEvent *ev = arg;
130    if (ev->type == EV_ABS) {
131        if (ev->code == ABS_X) {    /* scale to actual screen */
132            ev->value = ev->value * VirtgpuGetXres() / QEMU_TABLET_LEN;
133            ev->code = ABS_MT_POSITION_X;   /* OHOS WMS only support this code for EV_ABS */
134        } else if (ev->code == ABS_Y) {
135            ev->value = ev->value * VirtgpuGetYres() / QEMU_TABLET_LEN;
136            ev->code = ABS_MT_POSITION_Y;
137        }
138    }
139    HidReportEvent(g_virtInputDev, ev->type, ev->code, ev->value);
140}
141
142static void VirtinHandleEv(struct Virtin *in)
143{
144    struct Virtq *q = &in->dev.vq[0];
145    uint16_t idx;
146    uint16_t add = 0;
147    HdfWork w;
148
149    q->avail->flag = VIRTQ_AVAIL_F_NO_INTERRUPT;
150    while (q->last != q->used->index) {
151        DSB;
152        idx = q->used->ring[q->last % q->qsz].id;
153
154        if (HdfWorkInit(&w, VirtinWorkCallback, &in->ev[idx]) == HDF_SUCCESS) {
155            (void)HdfAddWork(&in->wq, &w);  /* HDF will alloc for 'realwork' */
156        }
157
158        q->avail->ring[(q->avail->index + add++) % q->qsz] = idx;
159        q->last++;
160    }
161    DSB;
162    q->avail->index += add;
163    q->avail->flag = 0;
164
165    if (q->used->flag != VIRTQ_USED_F_NO_NOTIFY) {
166        OSAL_WRITEL(0, in->dev.base + VIRTMMIO_REG_QUEUENOTIFY);
167    }
168}
169
170static uint32_t VirtinIRQhandle(uint32_t swIrq, void *dev)
171{
172    (void)swIrq;
173    struct Virtin *in = dev;
174
175    if (!(OSAL_READL(in->dev.base + VIRTMMIO_REG_INTERRUPTSTATUS) & VIRTMMIO_IRQ_NOTIFY_USED)) {
176        return 1;
177    }
178
179    VirtinHandleEv(in);
180
181    OSAL_WRITEL(VIRTMMIO_IRQ_NOTIFY_USED, in->dev.base + VIRTMMIO_REG_INTERRUPTACK);
182    return 0;
183}
184
185static void VirtinFillHidAbsInfo(struct VirtinConfig *conf, HidInfo *devInfo)
186{
187    int32_t i;
188
189    for (i = 0; i < HDF_ABS_CNT; i++) {
190        DSB;
191        conf->select = VIRTIO_INPUT_CFG_ABS_INFO;
192        conf->subsel = i;
193        DSB;
194        if (conf->size == 0) {
195            continue;
196        }
197        devInfo->axisInfo[i].axis = i;
198        devInfo->axisInfo[i].min = (int32_t)conf->u.abs.min;
199        devInfo->axisInfo[i].max = (int32_t)conf->u.abs.max;
200        devInfo->axisInfo[i].fuzz = (int32_t)conf->u.abs.fuzz;
201        devInfo->axisInfo[i].flat = (int32_t)conf->u.abs.flat;
202        devInfo->axisInfo[i].range = (int32_t)conf->u.abs.res;
203    }
204}
205
206static void VirtinFillHidCodeBitmap(struct VirtinConfig *conf, HidInfo *devInfo)
207{
208    uint8_t *qDest = NULL;
209    uint32_t i, evType, len;
210
211    devInfo->eventType[0] = 0;
212    for (evType = 0; evType < HDF_EV_CNT; evType++) {
213        DSB;
214        conf->select = VIRTIO_INPUT_CFG_EV_BITS;
215        conf->subsel = evType;
216        DSB;
217        if (conf->size == 0) {
218            continue;
219        }
220        switch (evType) {
221            case EV_KEY:
222                len = DIV_ROUND_UP(HDF_KEY_CNT, BYTE_HAS_BITS);
223                qDest = (uint8_t *)devInfo->keyCode;
224                break;
225            case EV_REL:
226                len = DIV_ROUND_UP(HDF_REL_CNT, BYTE_HAS_BITS);
227                qDest = (uint8_t *)devInfo->relCode;
228                break;
229            case EV_ABS:
230                len = DIV_ROUND_UP(HDF_ABS_CNT, BYTE_HAS_BITS);
231                qDest = (uint8_t *)devInfo->absCode;
232                break;
233            default:
234                HDF_LOGW("[%s]unsupported event type: %d", __func__, evType);
235                continue;
236        }
237        devInfo->eventType[0] |= 1 << evType;
238        for (i = 0; i < len && i < VIRTIN_PROP_LEN; i++) {
239            qDest[i] = conf->u.bitmap[i];
240        }
241    }
242}
243
244static void VirtinFillHidDevIds(struct VirtinConfig *conf, HidInfo *devInfo)
245{
246    conf->select = VIRTIO_INPUT_CFG_ID_DEVIDS;
247    conf->subsel = 0;
248    DSB;
249    if (conf->size) {
250        devInfo->bustype = conf->u.ids.bus;
251        devInfo->vendor = conf->u.ids.vendor;
252        devInfo->product = conf->u.ids.product;
253        devInfo->version = conf->u.ids.version;
254    }
255}
256
257static void VirtinFillHidInfo(const struct Virtin *in, HidInfo *devInfo)
258{
259    struct VirtinConfig *conf = (struct VirtinConfig *)(in->dev.base + VIRTMMIO_REG_CONFIG);
260    uint32_t before, after;
261
262    devInfo->devType = INDEV_TYPE_MOUSE;    /* only mouse and keyboard available */
263    devInfo->devName = VIRTMMIO_INPUT_NAME;
264
265    do {
266        before = OSAL_READL(in->dev.base + VIRTMMIO_REG_CONFIGGENERATION);
267
268        VirtinFillHidDevIds(conf, devInfo);
269        VirtinFillHidCodeBitmap(conf, devInfo);
270        VirtinFillHidAbsInfo(conf, devInfo);
271
272        after = OSAL_READL(in->dev.base + VIRTMMIO_REG_CONFIGGENERATION);
273    } while (before != after);
274}
275
276static int32_t HdfVirtinInitHid(struct Virtin *in)
277{
278    int32_t ret = HDF_SUCCESS;
279
280    HidInfo *devInfo = OsalMemCalloc(sizeof(HidInfo));
281    if (devInfo == NULL) {
282        HDF_LOGE("[%s]alloc HidInfo memory failed", __func__);
283        return HDF_ERR_MALLOC_FAIL;
284    }
285
286    VirtinFillHidInfo(in, devInfo);
287    SendInfoToHdf(devInfo);
288
289    g_virtInputDev = HidRegisterHdfInputDev(devInfo);
290    if (g_virtInputDev == NULL) {
291        HDF_LOGE("[%s]register input device failed", __func__);
292        ret = HDF_FAILURE;
293    }
294
295    OsalMemFree(devInfo);
296    return ret;
297}
298
299static void VirtinDeInit(struct Virtin *in)
300{
301    if (in->dev.irq & ~_IRQ_MASK) {
302        OsalUnregisterIrq(in->dev.irq & _IRQ_MASK, in);
303    }
304    LOS_DmaMemFree(in);
305}
306
307static struct Virtin *VirtinInitDev(void)
308{
309    struct Virtin *in = NULL;
310    VADDR_T base;
311    uint16_t qsz[VIRTQ_NUM];
312    int32_t ret, len;
313
314    len = sizeof(struct Virtin) + VirtqSize(VIRTQ_EVENT_QSZ) + VirtqSize(VIRTQ_STATUS_QSZ);
315    in = LOS_DmaMemAlloc(NULL, len, sizeof(void *), DMA_CACHE);
316    if (in == NULL) {
317        HDF_LOGE("[%s]alloc virtio-input memory failed", __func__);
318        return NULL;
319    }
320
321    if (!VirtmmioDiscover(VIRTMMIO_DEVICE_ID_INPUT, &in->dev)) {
322        goto ERR_OUT;
323    }
324
325    VirtmmioInitBegin(&in->dev);
326
327    if (!VirtmmioNegotiate(&in->dev, Feature0, Feature1, in)) {
328        goto ERR_OUT1;
329    }
330
331    base = ALIGN((VADDR_T)in + sizeof(struct Virtin), VIRTQ_ALIGN_DESC);
332    qsz[0] = VIRTQ_EVENT_QSZ;
333    qsz[1] = VIRTQ_STATUS_QSZ;
334    if (VirtmmioConfigQueue(&in->dev, base, qsz, VIRTQ_NUM) == 0) {
335        goto ERR_OUT1;
336    }
337
338    ret = OsalRegisterIrq(in->dev.irq, OSAL_IRQF_TRIGGER_NONE, (OsalIRQHandle)VirtinIRQhandle,
339                                                                VIRTMMIO_INPUT_NAME, in);
340    if (ret != HDF_SUCCESS) {
341        HDF_LOGE("[%s]register IRQ failed: %d", __func__, ret);
342        goto ERR_OUT1;
343    }
344    in->dev.irq |= ~_IRQ_MASK;
345
346    return in;
347
348ERR_OUT1:
349    VirtmmioInitFailed(&in->dev);
350ERR_OUT:
351    VirtinDeInit(in);
352    return NULL;
353}
354
355static int32_t HdfVirtinInit(struct HdfDeviceObject *device)
356{
357    struct Virtin *in = NULL;
358    int32_t ret;
359
360    if (device == NULL) {
361        HDF_LOGE("[%s]device is null", __func__);
362        return HDF_ERR_INVALID_PARAM;
363    }
364
365    if ((in = VirtinInitDev()) == NULL) {
366        return HDF_FAILURE;
367    }
368    device->priv = in;
369
370    if ((ret = HdfVirtinInitHid(in)) != HDF_SUCCESS) {
371        return ret;
372    }
373
374    if ((ret = HdfWorkQueueInit(&in->wq, VIRTMMIO_INPUT_NAME)) != HDF_SUCCESS) {
375        return ret;
376    }
377
378    PopulateEventQ(in);
379    VritmmioInitEnd(&in->dev);  /* now virt queue can be used */
380    return HDF_SUCCESS;
381}
382
383static void HdfVirtinRelease(struct HdfDeviceObject *deviceObject)
384{
385    if (deviceObject == NULL) {
386        return;
387    }
388
389    struct Virtin *in = deviceObject->priv;
390    if (in == NULL) {
391        return;
392    }
393
394    if (in->wq.realWorkQueue) {
395        HdfWorkQueueDestroy(&in->wq);
396    }
397    if (g_virtInputDev) {
398        HidUnregisterHdfInputDev(g_virtInputDev);
399    }
400    VirtinDeInit(in);
401}
402
403struct HdfDriverEntry g_virtInputEntry = {
404    .moduleVersion = 1,
405    .moduleName = "HDF_VIRTIO_MOUSE",
406    .Init = HdfVirtinInit,
407    .Release = HdfVirtinRelease,
408};
409
410HDF_INIT(g_virtInputEntry);
411