18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci#include <linux/module.h> 38c2ecf20Sopenharmony_ci#include <linux/virtio.h> 48c2ecf20Sopenharmony_ci#include <linux/virtio_config.h> 58c2ecf20Sopenharmony_ci#include <linux/input.h> 68c2ecf20Sopenharmony_ci#include <linux/slab.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <uapi/linux/virtio_ids.h> 98c2ecf20Sopenharmony_ci#include <uapi/linux/virtio_input.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_cistruct virtio_input { 128c2ecf20Sopenharmony_ci struct virtio_device *vdev; 138c2ecf20Sopenharmony_ci struct input_dev *idev; 148c2ecf20Sopenharmony_ci char name[64]; 158c2ecf20Sopenharmony_ci char serial[64]; 168c2ecf20Sopenharmony_ci char phys[64]; 178c2ecf20Sopenharmony_ci struct virtqueue *evt, *sts; 188c2ecf20Sopenharmony_ci struct virtio_input_event evts[64]; 198c2ecf20Sopenharmony_ci spinlock_t lock; 208c2ecf20Sopenharmony_ci bool ready; 218c2ecf20Sopenharmony_ci}; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic void virtinput_queue_evtbuf(struct virtio_input *vi, 248c2ecf20Sopenharmony_ci struct virtio_input_event *evtbuf) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci struct scatterlist sg[1]; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci sg_init_one(sg, evtbuf, sizeof(*evtbuf)); 298c2ecf20Sopenharmony_ci virtqueue_add_inbuf(vi->evt, sg, 1, evtbuf, GFP_ATOMIC); 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic void virtinput_recv_events(struct virtqueue *vq) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct virtio_input *vi = vq->vdev->priv; 358c2ecf20Sopenharmony_ci struct virtio_input_event *event; 368c2ecf20Sopenharmony_ci unsigned long flags; 378c2ecf20Sopenharmony_ci unsigned int len; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci spin_lock_irqsave(&vi->lock, flags); 408c2ecf20Sopenharmony_ci if (vi->ready) { 418c2ecf20Sopenharmony_ci while ((event = virtqueue_get_buf(vi->evt, &len)) != NULL) { 428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vi->lock, flags); 438c2ecf20Sopenharmony_ci input_event(vi->idev, 448c2ecf20Sopenharmony_ci le16_to_cpu(event->type), 458c2ecf20Sopenharmony_ci le16_to_cpu(event->code), 468c2ecf20Sopenharmony_ci le32_to_cpu(event->value)); 478c2ecf20Sopenharmony_ci spin_lock_irqsave(&vi->lock, flags); 488c2ecf20Sopenharmony_ci virtinput_queue_evtbuf(vi, event); 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci virtqueue_kick(vq); 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vi->lock, flags); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* 568c2ecf20Sopenharmony_ci * On error we are losing the status update, which isn't critical as 578c2ecf20Sopenharmony_ci * this is typically used for stuff like keyboard leds. 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_cistatic int virtinput_send_status(struct virtio_input *vi, 608c2ecf20Sopenharmony_ci u16 type, u16 code, s32 value) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct virtio_input_event *stsbuf; 638c2ecf20Sopenharmony_ci struct scatterlist sg[1]; 648c2ecf20Sopenharmony_ci unsigned long flags; 658c2ecf20Sopenharmony_ci int rc; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci stsbuf = kzalloc(sizeof(*stsbuf), GFP_ATOMIC); 688c2ecf20Sopenharmony_ci if (!stsbuf) 698c2ecf20Sopenharmony_ci return -ENOMEM; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci stsbuf->type = cpu_to_le16(type); 728c2ecf20Sopenharmony_ci stsbuf->code = cpu_to_le16(code); 738c2ecf20Sopenharmony_ci stsbuf->value = cpu_to_le32(value); 748c2ecf20Sopenharmony_ci sg_init_one(sg, stsbuf, sizeof(*stsbuf)); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci spin_lock_irqsave(&vi->lock, flags); 778c2ecf20Sopenharmony_ci if (vi->ready) { 788c2ecf20Sopenharmony_ci rc = virtqueue_add_outbuf(vi->sts, sg, 1, stsbuf, GFP_ATOMIC); 798c2ecf20Sopenharmony_ci virtqueue_kick(vi->sts); 808c2ecf20Sopenharmony_ci } else { 818c2ecf20Sopenharmony_ci rc = -ENODEV; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vi->lock, flags); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (rc != 0) 868c2ecf20Sopenharmony_ci kfree(stsbuf); 878c2ecf20Sopenharmony_ci return rc; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic void virtinput_recv_status(struct virtqueue *vq) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct virtio_input *vi = vq->vdev->priv; 938c2ecf20Sopenharmony_ci struct virtio_input_event *stsbuf; 948c2ecf20Sopenharmony_ci unsigned long flags; 958c2ecf20Sopenharmony_ci unsigned int len; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci spin_lock_irqsave(&vi->lock, flags); 988c2ecf20Sopenharmony_ci while ((stsbuf = virtqueue_get_buf(vi->sts, &len)) != NULL) 998c2ecf20Sopenharmony_ci kfree(stsbuf); 1008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vi->lock, flags); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int virtinput_status(struct input_dev *idev, unsigned int type, 1048c2ecf20Sopenharmony_ci unsigned int code, int value) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct virtio_input *vi = input_get_drvdata(idev); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return virtinput_send_status(vi, type, code, value); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic u8 virtinput_cfg_select(struct virtio_input *vi, 1128c2ecf20Sopenharmony_ci u8 select, u8 subsel) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci u8 size; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci virtio_cwrite_le(vi->vdev, struct virtio_input_config, select, &select); 1178c2ecf20Sopenharmony_ci virtio_cwrite_le(vi->vdev, struct virtio_input_config, subsel, &subsel); 1188c2ecf20Sopenharmony_ci virtio_cread_le(vi->vdev, struct virtio_input_config, size, &size); 1198c2ecf20Sopenharmony_ci return size; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic void virtinput_cfg_bits(struct virtio_input *vi, int select, int subsel, 1238c2ecf20Sopenharmony_ci unsigned long *bits, unsigned int bitcount) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci unsigned int bit; 1268c2ecf20Sopenharmony_ci u8 *virtio_bits; 1278c2ecf20Sopenharmony_ci u8 bytes; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci bytes = virtinput_cfg_select(vi, select, subsel); 1308c2ecf20Sopenharmony_ci if (!bytes) 1318c2ecf20Sopenharmony_ci return; 1328c2ecf20Sopenharmony_ci if (bitcount > bytes * 8) 1338c2ecf20Sopenharmony_ci bitcount = bytes * 8; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* 1368c2ecf20Sopenharmony_ci * Bitmap in virtio config space is a simple stream of bytes, 1378c2ecf20Sopenharmony_ci * with the first byte carrying bits 0-7, second bits 8-15 and 1388c2ecf20Sopenharmony_ci * so on. 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ci virtio_bits = kzalloc(bytes, GFP_KERNEL); 1418c2ecf20Sopenharmony_ci if (!virtio_bits) 1428c2ecf20Sopenharmony_ci return; 1438c2ecf20Sopenharmony_ci virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config, 1448c2ecf20Sopenharmony_ci u.bitmap), 1458c2ecf20Sopenharmony_ci virtio_bits, bytes); 1468c2ecf20Sopenharmony_ci for (bit = 0; bit < bitcount; bit++) { 1478c2ecf20Sopenharmony_ci if (virtio_bits[bit / 8] & (1 << (bit % 8))) 1488c2ecf20Sopenharmony_ci __set_bit(bit, bits); 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci kfree(virtio_bits); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (select == VIRTIO_INPUT_CFG_EV_BITS) 1538c2ecf20Sopenharmony_ci __set_bit(subsel, vi->idev->evbit); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic void virtinput_cfg_abs(struct virtio_input *vi, int abs) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci u32 mi, ma, re, fu, fl; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ABS_INFO, abs); 1618c2ecf20Sopenharmony_ci virtio_cread_le(vi->vdev, struct virtio_input_config, u.abs.min, &mi); 1628c2ecf20Sopenharmony_ci virtio_cread_le(vi->vdev, struct virtio_input_config, u.abs.max, &ma); 1638c2ecf20Sopenharmony_ci virtio_cread_le(vi->vdev, struct virtio_input_config, u.abs.res, &re); 1648c2ecf20Sopenharmony_ci virtio_cread_le(vi->vdev, struct virtio_input_config, u.abs.fuzz, &fu); 1658c2ecf20Sopenharmony_ci virtio_cread_le(vi->vdev, struct virtio_input_config, u.abs.flat, &fl); 1668c2ecf20Sopenharmony_ci input_set_abs_params(vi->idev, abs, mi, ma, fu, fl); 1678c2ecf20Sopenharmony_ci input_abs_set_res(vi->idev, abs, re); 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic int virtinput_init_vqs(struct virtio_input *vi) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct virtqueue *vqs[2]; 1738c2ecf20Sopenharmony_ci vq_callback_t *cbs[] = { virtinput_recv_events, 1748c2ecf20Sopenharmony_ci virtinput_recv_status }; 1758c2ecf20Sopenharmony_ci static const char * const names[] = { "events", "status" }; 1768c2ecf20Sopenharmony_ci int err; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci err = virtio_find_vqs(vi->vdev, 2, vqs, cbs, names, NULL); 1798c2ecf20Sopenharmony_ci if (err) 1808c2ecf20Sopenharmony_ci return err; 1818c2ecf20Sopenharmony_ci vi->evt = vqs[0]; 1828c2ecf20Sopenharmony_ci vi->sts = vqs[1]; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci return 0; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic void virtinput_fill_evt(struct virtio_input *vi) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci unsigned long flags; 1908c2ecf20Sopenharmony_ci int i, size; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci spin_lock_irqsave(&vi->lock, flags); 1938c2ecf20Sopenharmony_ci size = virtqueue_get_vring_size(vi->evt); 1948c2ecf20Sopenharmony_ci if (size > ARRAY_SIZE(vi->evts)) 1958c2ecf20Sopenharmony_ci size = ARRAY_SIZE(vi->evts); 1968c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) 1978c2ecf20Sopenharmony_ci virtinput_queue_evtbuf(vi, &vi->evts[i]); 1988c2ecf20Sopenharmony_ci virtqueue_kick(vi->evt); 1998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vi->lock, flags); 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic int virtinput_probe(struct virtio_device *vdev) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci struct virtio_input *vi; 2058c2ecf20Sopenharmony_ci unsigned long flags; 2068c2ecf20Sopenharmony_ci size_t size; 2078c2ecf20Sopenharmony_ci int abs, err; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) 2108c2ecf20Sopenharmony_ci return -ENODEV; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci vi = kzalloc(sizeof(*vi), GFP_KERNEL); 2138c2ecf20Sopenharmony_ci if (!vi) 2148c2ecf20Sopenharmony_ci return -ENOMEM; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci vdev->priv = vi; 2178c2ecf20Sopenharmony_ci vi->vdev = vdev; 2188c2ecf20Sopenharmony_ci spin_lock_init(&vi->lock); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci err = virtinput_init_vqs(vi); 2218c2ecf20Sopenharmony_ci if (err) 2228c2ecf20Sopenharmony_ci goto err_init_vq; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci vi->idev = input_allocate_device(); 2258c2ecf20Sopenharmony_ci if (!vi->idev) { 2268c2ecf20Sopenharmony_ci err = -ENOMEM; 2278c2ecf20Sopenharmony_ci goto err_input_alloc; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci input_set_drvdata(vi->idev, vi); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_NAME, 0); 2328c2ecf20Sopenharmony_ci virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config, 2338c2ecf20Sopenharmony_ci u.string), 2348c2ecf20Sopenharmony_ci vi->name, min(size, sizeof(vi->name))); 2358c2ecf20Sopenharmony_ci size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_SERIAL, 0); 2368c2ecf20Sopenharmony_ci virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config, 2378c2ecf20Sopenharmony_ci u.string), 2388c2ecf20Sopenharmony_ci vi->serial, min(size, sizeof(vi->serial))); 2398c2ecf20Sopenharmony_ci snprintf(vi->phys, sizeof(vi->phys), 2408c2ecf20Sopenharmony_ci "virtio%d/input0", vdev->index); 2418c2ecf20Sopenharmony_ci vi->idev->name = vi->name; 2428c2ecf20Sopenharmony_ci vi->idev->phys = vi->phys; 2438c2ecf20Sopenharmony_ci vi->idev->uniq = vi->serial; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_DEVIDS, 0); 2468c2ecf20Sopenharmony_ci if (size >= sizeof(struct virtio_input_devids)) { 2478c2ecf20Sopenharmony_ci virtio_cread_le(vi->vdev, struct virtio_input_config, 2488c2ecf20Sopenharmony_ci u.ids.bustype, &vi->idev->id.bustype); 2498c2ecf20Sopenharmony_ci virtio_cread_le(vi->vdev, struct virtio_input_config, 2508c2ecf20Sopenharmony_ci u.ids.vendor, &vi->idev->id.vendor); 2518c2ecf20Sopenharmony_ci virtio_cread_le(vi->vdev, struct virtio_input_config, 2528c2ecf20Sopenharmony_ci u.ids.product, &vi->idev->id.product); 2538c2ecf20Sopenharmony_ci virtio_cread_le(vi->vdev, struct virtio_input_config, 2548c2ecf20Sopenharmony_ci u.ids.version, &vi->idev->id.version); 2558c2ecf20Sopenharmony_ci } else { 2568c2ecf20Sopenharmony_ci vi->idev->id.bustype = BUS_VIRTUAL; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_PROP_BITS, 0, 2608c2ecf20Sopenharmony_ci vi->idev->propbit, INPUT_PROP_CNT); 2618c2ecf20Sopenharmony_ci size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_REP); 2628c2ecf20Sopenharmony_ci if (size) 2638c2ecf20Sopenharmony_ci __set_bit(EV_REP, vi->idev->evbit); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci vi->idev->dev.parent = &vdev->dev; 2668c2ecf20Sopenharmony_ci vi->idev->event = virtinput_status; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* device -> kernel */ 2698c2ecf20Sopenharmony_ci virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_KEY, 2708c2ecf20Sopenharmony_ci vi->idev->keybit, KEY_CNT); 2718c2ecf20Sopenharmony_ci virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_REL, 2728c2ecf20Sopenharmony_ci vi->idev->relbit, REL_CNT); 2738c2ecf20Sopenharmony_ci virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_ABS, 2748c2ecf20Sopenharmony_ci vi->idev->absbit, ABS_CNT); 2758c2ecf20Sopenharmony_ci virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_MSC, 2768c2ecf20Sopenharmony_ci vi->idev->mscbit, MSC_CNT); 2778c2ecf20Sopenharmony_ci virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_SW, 2788c2ecf20Sopenharmony_ci vi->idev->swbit, SW_CNT); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* kernel -> device */ 2818c2ecf20Sopenharmony_ci virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_LED, 2828c2ecf20Sopenharmony_ci vi->idev->ledbit, LED_CNT); 2838c2ecf20Sopenharmony_ci virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_SND, 2848c2ecf20Sopenharmony_ci vi->idev->sndbit, SND_CNT); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (test_bit(EV_ABS, vi->idev->evbit)) { 2878c2ecf20Sopenharmony_ci for (abs = 0; abs < ABS_CNT; abs++) { 2888c2ecf20Sopenharmony_ci if (!test_bit(abs, vi->idev->absbit)) 2898c2ecf20Sopenharmony_ci continue; 2908c2ecf20Sopenharmony_ci virtinput_cfg_abs(vi, abs); 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci virtio_device_ready(vdev); 2958c2ecf20Sopenharmony_ci vi->ready = true; 2968c2ecf20Sopenharmony_ci err = input_register_device(vi->idev); 2978c2ecf20Sopenharmony_ci if (err) 2988c2ecf20Sopenharmony_ci goto err_input_register; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci virtinput_fill_evt(vi); 3018c2ecf20Sopenharmony_ci return 0; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cierr_input_register: 3048c2ecf20Sopenharmony_ci spin_lock_irqsave(&vi->lock, flags); 3058c2ecf20Sopenharmony_ci vi->ready = false; 3068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vi->lock, flags); 3078c2ecf20Sopenharmony_ci input_free_device(vi->idev); 3088c2ecf20Sopenharmony_cierr_input_alloc: 3098c2ecf20Sopenharmony_ci vdev->config->del_vqs(vdev); 3108c2ecf20Sopenharmony_cierr_init_vq: 3118c2ecf20Sopenharmony_ci kfree(vi); 3128c2ecf20Sopenharmony_ci return err; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic void virtinput_remove(struct virtio_device *vdev) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct virtio_input *vi = vdev->priv; 3188c2ecf20Sopenharmony_ci void *buf; 3198c2ecf20Sopenharmony_ci unsigned long flags; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci spin_lock_irqsave(&vi->lock, flags); 3228c2ecf20Sopenharmony_ci vi->ready = false; 3238c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vi->lock, flags); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci input_unregister_device(vi->idev); 3268c2ecf20Sopenharmony_ci vdev->config->reset(vdev); 3278c2ecf20Sopenharmony_ci while ((buf = virtqueue_detach_unused_buf(vi->sts)) != NULL) 3288c2ecf20Sopenharmony_ci kfree(buf); 3298c2ecf20Sopenharmony_ci vdev->config->del_vqs(vdev); 3308c2ecf20Sopenharmony_ci kfree(vi); 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 3348c2ecf20Sopenharmony_cistatic int virtinput_freeze(struct virtio_device *vdev) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci struct virtio_input *vi = vdev->priv; 3378c2ecf20Sopenharmony_ci unsigned long flags; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci spin_lock_irqsave(&vi->lock, flags); 3408c2ecf20Sopenharmony_ci vi->ready = false; 3418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vi->lock, flags); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci vdev->config->del_vqs(vdev); 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic int virtinput_restore(struct virtio_device *vdev) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci struct virtio_input *vi = vdev->priv; 3508c2ecf20Sopenharmony_ci int err; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci err = virtinput_init_vqs(vi); 3538c2ecf20Sopenharmony_ci if (err) 3548c2ecf20Sopenharmony_ci return err; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci virtio_device_ready(vdev); 3578c2ecf20Sopenharmony_ci vi->ready = true; 3588c2ecf20Sopenharmony_ci virtinput_fill_evt(vi); 3598c2ecf20Sopenharmony_ci return 0; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci#endif 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic unsigned int features[] = { 3648c2ecf20Sopenharmony_ci /* none */ 3658c2ecf20Sopenharmony_ci}; 3668c2ecf20Sopenharmony_cistatic const struct virtio_device_id id_table[] = { 3678c2ecf20Sopenharmony_ci { VIRTIO_ID_INPUT, VIRTIO_DEV_ANY_ID }, 3688c2ecf20Sopenharmony_ci { 0 }, 3698c2ecf20Sopenharmony_ci}; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic struct virtio_driver virtio_input_driver = { 3728c2ecf20Sopenharmony_ci .driver.name = KBUILD_MODNAME, 3738c2ecf20Sopenharmony_ci .driver.owner = THIS_MODULE, 3748c2ecf20Sopenharmony_ci .feature_table = features, 3758c2ecf20Sopenharmony_ci .feature_table_size = ARRAY_SIZE(features), 3768c2ecf20Sopenharmony_ci .id_table = id_table, 3778c2ecf20Sopenharmony_ci .probe = virtinput_probe, 3788c2ecf20Sopenharmony_ci .remove = virtinput_remove, 3798c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 3808c2ecf20Sopenharmony_ci .freeze = virtinput_freeze, 3818c2ecf20Sopenharmony_ci .restore = virtinput_restore, 3828c2ecf20Sopenharmony_ci#endif 3838c2ecf20Sopenharmony_ci}; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cimodule_virtio_driver(virtio_input_driver); 3868c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(virtio, id_table); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3898c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Virtio input device driver"); 3908c2ecf20Sopenharmony_ciMODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>"); 391