| 1 | // SPDX-License-Identifier: GPL-2.0-only | 
|---|
| 2 | #include <linux/module.h> | 
|---|
| 3 | #include <linux/virtio.h> | 
|---|
| 4 | #include <linux/virtio_config.h> | 
|---|
| 5 | #include <linux/input.h> | 
|---|
| 6 | #include <linux/slab.h> | 
|---|
| 7 |  | 
|---|
| 8 | #include <uapi/linux/virtio_ids.h> | 
|---|
| 9 | #include <uapi/linux/virtio_input.h> | 
|---|
| 10 | #include <linux/input/mt.h> | 
|---|
| 11 |  | 
|---|
| 12 | struct virtio_input { | 
|---|
| 13 | struct virtio_device       *vdev; | 
|---|
| 14 | struct input_dev           *idev; | 
|---|
| 15 | char                       name[64]; | 
|---|
| 16 | char                       serial[64]; | 
|---|
| 17 | char                       phys[64]; | 
|---|
| 18 | struct virtqueue           *evt, *sts; | 
|---|
| 19 | struct virtio_input_event  evts[64]; | 
|---|
| 20 | spinlock_t                 lock; | 
|---|
| 21 | bool                       ready; | 
|---|
| 22 | }; | 
|---|
| 23 |  | 
|---|
| 24 | static void virtinput_queue_evtbuf(struct virtio_input *vi, | 
|---|
| 25 | struct virtio_input_event *evtbuf) | 
|---|
| 26 | { | 
|---|
| 27 | struct scatterlist sg[1]; | 
|---|
| 28 |  | 
|---|
| 29 | sg_init_one(sg, evtbuf, sizeof(*evtbuf)); | 
|---|
| 30 | virtqueue_add_inbuf(vq: vi->evt, sg, num: 1, data: evtbuf, GFP_ATOMIC); | 
|---|
| 31 | } | 
|---|
| 32 |  | 
|---|
| 33 | static void virtinput_recv_events(struct virtqueue *vq) | 
|---|
| 34 | { | 
|---|
| 35 | struct virtio_input *vi = vq->vdev->priv; | 
|---|
| 36 | struct virtio_input_event *event; | 
|---|
| 37 | unsigned long flags; | 
|---|
| 38 | unsigned int len; | 
|---|
| 39 |  | 
|---|
| 40 | spin_lock_irqsave(&vi->lock, flags); | 
|---|
| 41 | if (vi->ready) { | 
|---|
| 42 | while ((event = virtqueue_get_buf(vq: vi->evt, len: &len)) != NULL) { | 
|---|
| 43 | spin_unlock_irqrestore(lock: &vi->lock, flags); | 
|---|
| 44 | input_event(dev: vi->idev, | 
|---|
| 45 | le16_to_cpu(event->type), | 
|---|
| 46 | le16_to_cpu(event->code), | 
|---|
| 47 | le32_to_cpu(event->value)); | 
|---|
| 48 | spin_lock_irqsave(&vi->lock, flags); | 
|---|
| 49 | virtinput_queue_evtbuf(vi, evtbuf: event); | 
|---|
| 50 | } | 
|---|
| 51 | virtqueue_kick(vq); | 
|---|
| 52 | } | 
|---|
| 53 | spin_unlock_irqrestore(lock: &vi->lock, flags); | 
|---|
| 54 | } | 
|---|
| 55 |  | 
|---|
| 56 | /* | 
|---|
| 57 | * On error we are losing the status update, which isn't critical as | 
|---|
| 58 | * this is typically used for stuff like keyboard leds. | 
|---|
| 59 | */ | 
|---|
| 60 | static int virtinput_send_status(struct virtio_input *vi, | 
|---|
| 61 | u16 type, u16 code, s32 value) | 
|---|
| 62 | { | 
|---|
| 63 | struct virtio_input_event *stsbuf; | 
|---|
| 64 | struct scatterlist sg[1]; | 
|---|
| 65 | unsigned long flags; | 
|---|
| 66 | int rc; | 
|---|
| 67 |  | 
|---|
| 68 | /* | 
|---|
| 69 | * Since 29cc309d8bf1 (HID: hid-multitouch: forward MSC_TIMESTAMP), | 
|---|
| 70 | * EV_MSC/MSC_TIMESTAMP is added to each before EV_SYN event. | 
|---|
| 71 | * EV_MSC is configured as INPUT_PASS_TO_ALL. | 
|---|
| 72 | * In case of touch device: | 
|---|
| 73 | *   BE pass EV_MSC/MSC_TIMESTAMP to FE on receiving event from evdev. | 
|---|
| 74 | *   FE pass EV_MSC/MSC_TIMESTAMP back to BE. | 
|---|
| 75 | *   BE writes EV_MSC/MSC_TIMESTAMP to evdev due to INPUT_PASS_TO_ALL. | 
|---|
| 76 | *   BE receives extra EV_MSC/MSC_TIMESTAMP and pass to FE. | 
|---|
| 77 | *   >>> Each new frame becomes larger and larger. | 
|---|
| 78 | * Disable EV_MSC/MSC_TIMESTAMP forwarding for MT. | 
|---|
| 79 | */ | 
|---|
| 80 | if (vi->idev->mt && type == EV_MSC && code == MSC_TIMESTAMP) | 
|---|
| 81 | return 0; | 
|---|
| 82 |  | 
|---|
| 83 | stsbuf = kzalloc(sizeof(*stsbuf), GFP_ATOMIC); | 
|---|
| 84 | if (!stsbuf) | 
|---|
| 85 | return -ENOMEM; | 
|---|
| 86 |  | 
|---|
| 87 | stsbuf->type  = cpu_to_le16(type); | 
|---|
| 88 | stsbuf->code  = cpu_to_le16(code); | 
|---|
| 89 | stsbuf->value = cpu_to_le32(value); | 
|---|
| 90 | sg_init_one(sg, stsbuf, sizeof(*stsbuf)); | 
|---|
| 91 |  | 
|---|
| 92 | spin_lock_irqsave(&vi->lock, flags); | 
|---|
| 93 | if (vi->ready) { | 
|---|
| 94 | rc = virtqueue_add_outbuf(vq: vi->sts, sg, num: 1, data: stsbuf, GFP_ATOMIC); | 
|---|
| 95 | virtqueue_kick(vq: vi->sts); | 
|---|
| 96 | } else { | 
|---|
| 97 | rc = -ENODEV; | 
|---|
| 98 | } | 
|---|
| 99 | spin_unlock_irqrestore(lock: &vi->lock, flags); | 
|---|
| 100 |  | 
|---|
| 101 | if (rc != 0) | 
|---|
| 102 | kfree(objp: stsbuf); | 
|---|
| 103 | return rc; | 
|---|
| 104 | } | 
|---|
| 105 |  | 
|---|
| 106 | static void virtinput_recv_status(struct virtqueue *vq) | 
|---|
| 107 | { | 
|---|
| 108 | struct virtio_input *vi = vq->vdev->priv; | 
|---|
| 109 | struct virtio_input_event *stsbuf; | 
|---|
| 110 | unsigned long flags; | 
|---|
| 111 | unsigned int len; | 
|---|
| 112 |  | 
|---|
| 113 | spin_lock_irqsave(&vi->lock, flags); | 
|---|
| 114 | while ((stsbuf = virtqueue_get_buf(vq: vi->sts, len: &len)) != NULL) | 
|---|
| 115 | kfree(objp: stsbuf); | 
|---|
| 116 | spin_unlock_irqrestore(lock: &vi->lock, flags); | 
|---|
| 117 | } | 
|---|
| 118 |  | 
|---|
| 119 | static int virtinput_status(struct input_dev *idev, unsigned int type, | 
|---|
| 120 | unsigned int code, int value) | 
|---|
| 121 | { | 
|---|
| 122 | struct virtio_input *vi = input_get_drvdata(dev: idev); | 
|---|
| 123 |  | 
|---|
| 124 | return virtinput_send_status(vi, type, code, value); | 
|---|
| 125 | } | 
|---|
| 126 |  | 
|---|
| 127 | static u8 virtinput_cfg_select(struct virtio_input *vi, | 
|---|
| 128 | u8 select, u8 subsel) | 
|---|
| 129 | { | 
|---|
| 130 | u8 size; | 
|---|
| 131 |  | 
|---|
| 132 | virtio_cwrite_le(vi->vdev, struct virtio_input_config, select, &select); | 
|---|
| 133 | virtio_cwrite_le(vi->vdev, struct virtio_input_config, subsel, &subsel); | 
|---|
| 134 | virtio_cread_le(vi->vdev, struct virtio_input_config, size, &size); | 
|---|
| 135 | return size; | 
|---|
| 136 | } | 
|---|
| 137 |  | 
|---|
| 138 | static void virtinput_cfg_bits(struct virtio_input *vi, int select, int subsel, | 
|---|
| 139 | unsigned long *bits, unsigned int bitcount) | 
|---|
| 140 | { | 
|---|
| 141 | unsigned int bit; | 
|---|
| 142 | u8 *virtio_bits; | 
|---|
| 143 | u8 bytes; | 
|---|
| 144 |  | 
|---|
| 145 | bytes = virtinput_cfg_select(vi, select, subsel); | 
|---|
| 146 | if (!bytes) | 
|---|
| 147 | return; | 
|---|
| 148 | if (bitcount > bytes * 8) | 
|---|
| 149 | bitcount = bytes * 8; | 
|---|
| 150 |  | 
|---|
| 151 | /* | 
|---|
| 152 | * Bitmap in virtio config space is a simple stream of bytes, | 
|---|
| 153 | * with the first byte carrying bits 0-7, second bits 8-15 and | 
|---|
| 154 | * so on. | 
|---|
| 155 | */ | 
|---|
| 156 | virtio_bits = kzalloc(bytes, GFP_KERNEL); | 
|---|
| 157 | if (!virtio_bits) | 
|---|
| 158 | return; | 
|---|
| 159 | virtio_cread_bytes(vdev: vi->vdev, offsetof(struct virtio_input_config, | 
|---|
| 160 | u.bitmap), | 
|---|
| 161 | buf: virtio_bits, len: bytes); | 
|---|
| 162 | for (bit = 0; bit < bitcount; bit++) { | 
|---|
| 163 | if (virtio_bits[bit / 8] & (1 << (bit % 8))) | 
|---|
| 164 | __set_bit(bit, bits); | 
|---|
| 165 | } | 
|---|
| 166 | kfree(objp: virtio_bits); | 
|---|
| 167 |  | 
|---|
| 168 | if (select == VIRTIO_INPUT_CFG_EV_BITS) | 
|---|
| 169 | __set_bit(subsel, vi->idev->evbit); | 
|---|
| 170 | } | 
|---|
| 171 |  | 
|---|
| 172 | static void virtinput_cfg_abs(struct virtio_input *vi, int abs) | 
|---|
| 173 | { | 
|---|
| 174 | u32 mi, ma, re, fu, fl; | 
|---|
| 175 |  | 
|---|
| 176 | virtinput_cfg_select(vi, select: VIRTIO_INPUT_CFG_ABS_INFO, subsel: abs); | 
|---|
| 177 | virtio_cread_le(vi->vdev, struct virtio_input_config, u.abs.min, &mi); | 
|---|
| 178 | virtio_cread_le(vi->vdev, struct virtio_input_config, u.abs.max, &ma); | 
|---|
| 179 | virtio_cread_le(vi->vdev, struct virtio_input_config, u.abs.res, &re); | 
|---|
| 180 | virtio_cread_le(vi->vdev, struct virtio_input_config, u.abs.fuzz, &fu); | 
|---|
| 181 | virtio_cread_le(vi->vdev, struct virtio_input_config, u.abs.flat, &fl); | 
|---|
| 182 | input_set_abs_params(dev: vi->idev, axis: abs, min: mi, max: ma, fuzz: fu, flat: fl); | 
|---|
| 183 | input_abs_set_res(dev: vi->idev, axis: abs, val: re); | 
|---|
| 184 | } | 
|---|
| 185 |  | 
|---|
| 186 | static int virtinput_init_vqs(struct virtio_input *vi) | 
|---|
| 187 | { | 
|---|
| 188 | struct virtqueue_info vqs_info[] = { | 
|---|
| 189 | { "events", virtinput_recv_events }, | 
|---|
| 190 | { "status", virtinput_recv_status }, | 
|---|
| 191 | }; | 
|---|
| 192 | struct virtqueue *vqs[2]; | 
|---|
| 193 | int err; | 
|---|
| 194 |  | 
|---|
| 195 | err = virtio_find_vqs(vdev: vi->vdev, nvqs: 2, vqs, vqs_info, NULL); | 
|---|
| 196 | if (err) | 
|---|
| 197 | return err; | 
|---|
| 198 | vi->evt = vqs[0]; | 
|---|
| 199 | vi->sts = vqs[1]; | 
|---|
| 200 |  | 
|---|
| 201 | return 0; | 
|---|
| 202 | } | 
|---|
| 203 |  | 
|---|
| 204 | static void virtinput_fill_evt(struct virtio_input *vi) | 
|---|
| 205 | { | 
|---|
| 206 | unsigned long flags; | 
|---|
| 207 | int i, size; | 
|---|
| 208 |  | 
|---|
| 209 | spin_lock_irqsave(&vi->lock, flags); | 
|---|
| 210 | size = virtqueue_get_vring_size(vq: vi->evt); | 
|---|
| 211 | if (size > ARRAY_SIZE(vi->evts)) | 
|---|
| 212 | size = ARRAY_SIZE(vi->evts); | 
|---|
| 213 | for (i = 0; i < size; i++) | 
|---|
| 214 | virtinput_queue_evtbuf(vi, evtbuf: &vi->evts[i]); | 
|---|
| 215 | virtqueue_kick(vq: vi->evt); | 
|---|
| 216 | spin_unlock_irqrestore(lock: &vi->lock, flags); | 
|---|
| 217 | } | 
|---|
| 218 |  | 
|---|
| 219 | static int virtinput_probe(struct virtio_device *vdev) | 
|---|
| 220 | { | 
|---|
| 221 | struct virtio_input *vi; | 
|---|
| 222 | unsigned long flags; | 
|---|
| 223 | size_t size; | 
|---|
| 224 | int abs, err, nslots; | 
|---|
| 225 |  | 
|---|
| 226 | if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) | 
|---|
| 227 | return -ENODEV; | 
|---|
| 228 |  | 
|---|
| 229 | vi = kzalloc(sizeof(*vi), GFP_KERNEL); | 
|---|
| 230 | if (!vi) | 
|---|
| 231 | return -ENOMEM; | 
|---|
| 232 |  | 
|---|
| 233 | vdev->priv = vi; | 
|---|
| 234 | vi->vdev = vdev; | 
|---|
| 235 | spin_lock_init(&vi->lock); | 
|---|
| 236 |  | 
|---|
| 237 | err = virtinput_init_vqs(vi); | 
|---|
| 238 | if (err) | 
|---|
| 239 | goto err_init_vq; | 
|---|
| 240 |  | 
|---|
| 241 | vi->idev = input_allocate_device(); | 
|---|
| 242 | if (!vi->idev) { | 
|---|
| 243 | err = -ENOMEM; | 
|---|
| 244 | goto err_input_alloc; | 
|---|
| 245 | } | 
|---|
| 246 | input_set_drvdata(dev: vi->idev, data: vi); | 
|---|
| 247 |  | 
|---|
| 248 | size = virtinput_cfg_select(vi, select: VIRTIO_INPUT_CFG_ID_NAME, subsel: 0); | 
|---|
| 249 | virtio_cread_bytes(vdev: vi->vdev, offsetof(struct virtio_input_config, | 
|---|
| 250 | u.string), | 
|---|
| 251 | buf: vi->name, min(size, sizeof(vi->name))); | 
|---|
| 252 | size = virtinput_cfg_select(vi, select: VIRTIO_INPUT_CFG_ID_SERIAL, subsel: 0); | 
|---|
| 253 | virtio_cread_bytes(vdev: vi->vdev, offsetof(struct virtio_input_config, | 
|---|
| 254 | u.string), | 
|---|
| 255 | buf: vi->serial, min(size, sizeof(vi->serial))); | 
|---|
| 256 | snprintf(buf: vi->phys, size: sizeof(vi->phys), | 
|---|
| 257 | fmt: "virtio%d/input0", vdev->index); | 
|---|
| 258 | vi->idev->name = vi->name; | 
|---|
| 259 | vi->idev->phys = vi->phys; | 
|---|
| 260 | vi->idev->uniq = vi->serial; | 
|---|
| 261 |  | 
|---|
| 262 | size = virtinput_cfg_select(vi, select: VIRTIO_INPUT_CFG_ID_DEVIDS, subsel: 0); | 
|---|
| 263 | if (size >= sizeof(struct virtio_input_devids)) { | 
|---|
| 264 | virtio_cread_le(vi->vdev, struct virtio_input_config, | 
|---|
| 265 | u.ids.bustype, &vi->idev->id.bustype); | 
|---|
| 266 | virtio_cread_le(vi->vdev, struct virtio_input_config, | 
|---|
| 267 | u.ids.vendor, &vi->idev->id.vendor); | 
|---|
| 268 | virtio_cread_le(vi->vdev, struct virtio_input_config, | 
|---|
| 269 | u.ids.product, &vi->idev->id.product); | 
|---|
| 270 | virtio_cread_le(vi->vdev, struct virtio_input_config, | 
|---|
| 271 | u.ids.version, &vi->idev->id.version); | 
|---|
| 272 | } else { | 
|---|
| 273 | vi->idev->id.bustype = BUS_VIRTUAL; | 
|---|
| 274 | } | 
|---|
| 275 |  | 
|---|
| 276 | virtinput_cfg_bits(vi, select: VIRTIO_INPUT_CFG_PROP_BITS, subsel: 0, | 
|---|
| 277 | bits: vi->idev->propbit, INPUT_PROP_CNT); | 
|---|
| 278 | size = virtinput_cfg_select(vi, select: VIRTIO_INPUT_CFG_EV_BITS, EV_REP); | 
|---|
| 279 | if (size) | 
|---|
| 280 | __set_bit(EV_REP, vi->idev->evbit); | 
|---|
| 281 |  | 
|---|
| 282 | vi->idev->dev.parent = &vdev->dev; | 
|---|
| 283 | vi->idev->event = virtinput_status; | 
|---|
| 284 |  | 
|---|
| 285 | /* device -> kernel */ | 
|---|
| 286 | virtinput_cfg_bits(vi, select: VIRTIO_INPUT_CFG_EV_BITS, EV_KEY, | 
|---|
| 287 | bits: vi->idev->keybit, KEY_CNT); | 
|---|
| 288 | virtinput_cfg_bits(vi, select: VIRTIO_INPUT_CFG_EV_BITS, EV_REL, | 
|---|
| 289 | bits: vi->idev->relbit, REL_CNT); | 
|---|
| 290 | virtinput_cfg_bits(vi, select: VIRTIO_INPUT_CFG_EV_BITS, EV_ABS, | 
|---|
| 291 | bits: vi->idev->absbit, ABS_CNT); | 
|---|
| 292 | virtinput_cfg_bits(vi, select: VIRTIO_INPUT_CFG_EV_BITS, EV_MSC, | 
|---|
| 293 | bits: vi->idev->mscbit, MSC_CNT); | 
|---|
| 294 | virtinput_cfg_bits(vi, select: VIRTIO_INPUT_CFG_EV_BITS, EV_SW, | 
|---|
| 295 | bits: vi->idev->swbit,  SW_CNT); | 
|---|
| 296 |  | 
|---|
| 297 | /* kernel -> device */ | 
|---|
| 298 | virtinput_cfg_bits(vi, select: VIRTIO_INPUT_CFG_EV_BITS, EV_LED, | 
|---|
| 299 | bits: vi->idev->ledbit, LED_CNT); | 
|---|
| 300 | virtinput_cfg_bits(vi, select: VIRTIO_INPUT_CFG_EV_BITS, EV_SND, | 
|---|
| 301 | bits: vi->idev->sndbit, SND_CNT); | 
|---|
| 302 |  | 
|---|
| 303 | if (test_bit(EV_ABS, vi->idev->evbit)) { | 
|---|
| 304 | for (abs = 0; abs < ABS_CNT; abs++) { | 
|---|
| 305 | if (!test_bit(abs, vi->idev->absbit)) | 
|---|
| 306 | continue; | 
|---|
| 307 | virtinput_cfg_abs(vi, abs); | 
|---|
| 308 | } | 
|---|
| 309 |  | 
|---|
| 310 | if (test_bit(ABS_MT_SLOT, vi->idev->absbit)) { | 
|---|
| 311 | nslots = input_abs_get_max(dev: vi->idev, ABS_MT_SLOT) + 1; | 
|---|
| 312 | err = input_mt_init_slots(dev: vi->idev, num_slots: nslots, flags: 0); | 
|---|
| 313 | if (err) | 
|---|
| 314 | goto err_mt_init_slots; | 
|---|
| 315 | } | 
|---|
| 316 | } | 
|---|
| 317 |  | 
|---|
| 318 | virtio_device_ready(dev: vdev); | 
|---|
| 319 | vi->ready = true; | 
|---|
| 320 | err = input_register_device(vi->idev); | 
|---|
| 321 | if (err) | 
|---|
| 322 | goto err_input_register; | 
|---|
| 323 |  | 
|---|
| 324 | virtinput_fill_evt(vi); | 
|---|
| 325 | return 0; | 
|---|
| 326 |  | 
|---|
| 327 | err_input_register: | 
|---|
| 328 | spin_lock_irqsave(&vi->lock, flags); | 
|---|
| 329 | vi->ready = false; | 
|---|
| 330 | spin_unlock_irqrestore(lock: &vi->lock, flags); | 
|---|
| 331 | err_mt_init_slots: | 
|---|
| 332 | input_free_device(dev: vi->idev); | 
|---|
| 333 | err_input_alloc: | 
|---|
| 334 | vdev->config->del_vqs(vdev); | 
|---|
| 335 | err_init_vq: | 
|---|
| 336 | kfree(objp: vi); | 
|---|
| 337 | return err; | 
|---|
| 338 | } | 
|---|
| 339 |  | 
|---|
| 340 | static void virtinput_remove(struct virtio_device *vdev) | 
|---|
| 341 | { | 
|---|
| 342 | struct virtio_input *vi = vdev->priv; | 
|---|
| 343 | void *buf; | 
|---|
| 344 | unsigned long flags; | 
|---|
| 345 |  | 
|---|
| 346 | spin_lock_irqsave(&vi->lock, flags); | 
|---|
| 347 | vi->ready = false; | 
|---|
| 348 | spin_unlock_irqrestore(lock: &vi->lock, flags); | 
|---|
| 349 |  | 
|---|
| 350 | input_unregister_device(vi->idev); | 
|---|
| 351 | virtio_reset_device(dev: vdev); | 
|---|
| 352 | while ((buf = virtqueue_detach_unused_buf(vq: vi->sts)) != NULL) | 
|---|
| 353 | kfree(objp: buf); | 
|---|
| 354 | vdev->config->del_vqs(vdev); | 
|---|
| 355 | kfree(objp: vi); | 
|---|
| 356 | } | 
|---|
| 357 |  | 
|---|
| 358 | #ifdef CONFIG_PM_SLEEP | 
|---|
| 359 | static int virtinput_freeze(struct virtio_device *vdev) | 
|---|
| 360 | { | 
|---|
| 361 | struct virtio_input *vi = vdev->priv; | 
|---|
| 362 | unsigned long flags; | 
|---|
| 363 | void *buf; | 
|---|
| 364 |  | 
|---|
| 365 | spin_lock_irqsave(&vi->lock, flags); | 
|---|
| 366 | vi->ready = false; | 
|---|
| 367 | spin_unlock_irqrestore(lock: &vi->lock, flags); | 
|---|
| 368 |  | 
|---|
| 369 | virtio_reset_device(dev: vdev); | 
|---|
| 370 | while ((buf = virtqueue_detach_unused_buf(vq: vi->sts)) != NULL) | 
|---|
| 371 | kfree(objp: buf); | 
|---|
| 372 | vdev->config->del_vqs(vdev); | 
|---|
| 373 | return 0; | 
|---|
| 374 | } | 
|---|
| 375 |  | 
|---|
| 376 | static int virtinput_restore(struct virtio_device *vdev) | 
|---|
| 377 | { | 
|---|
| 378 | struct virtio_input *vi = vdev->priv; | 
|---|
| 379 | int err; | 
|---|
| 380 |  | 
|---|
| 381 | err = virtinput_init_vqs(vi); | 
|---|
| 382 | if (err) | 
|---|
| 383 | return err; | 
|---|
| 384 |  | 
|---|
| 385 | virtio_device_ready(dev: vdev); | 
|---|
| 386 | vi->ready = true; | 
|---|
| 387 | virtinput_fill_evt(vi); | 
|---|
| 388 | return 0; | 
|---|
| 389 | } | 
|---|
| 390 | #endif | 
|---|
| 391 |  | 
|---|
| 392 | static unsigned int features[] = { | 
|---|
| 393 | /* none */ | 
|---|
| 394 | }; | 
|---|
| 395 | static const struct virtio_device_id id_table[] = { | 
|---|
| 396 | { VIRTIO_ID_INPUT, VIRTIO_DEV_ANY_ID }, | 
|---|
| 397 | { 0 }, | 
|---|
| 398 | }; | 
|---|
| 399 |  | 
|---|
| 400 | static struct virtio_driver virtio_input_driver = { | 
|---|
| 401 | .driver.name         = KBUILD_MODNAME, | 
|---|
| 402 | .feature_table       = features, | 
|---|
| 403 | .feature_table_size  = ARRAY_SIZE(features), | 
|---|
| 404 | .id_table            = id_table, | 
|---|
| 405 | .probe               = virtinput_probe, | 
|---|
| 406 | .remove              = virtinput_remove, | 
|---|
| 407 | #ifdef CONFIG_PM_SLEEP | 
|---|
| 408 | .freeze	             = virtinput_freeze, | 
|---|
| 409 | .restore             = virtinput_restore, | 
|---|
| 410 | #endif | 
|---|
| 411 | }; | 
|---|
| 412 |  | 
|---|
| 413 | module_virtio_driver(virtio_input_driver); | 
|---|
| 414 | MODULE_DEVICE_TABLE(virtio, id_table); | 
|---|
| 415 |  | 
|---|
| 416 | MODULE_LICENSE( "GPL"); | 
|---|
| 417 | MODULE_DESCRIPTION( "Virtio input device driver"); | 
|---|
| 418 | MODULE_AUTHOR( "Gerd Hoffmann <kraxel@redhat.com>"); | 
|---|
| 419 |  | 
|---|