1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13/**
14 * @brief USB HID Mouse driver
15 * @see USB HID spec Appendix B
16 */
17#include <string.h>
18#include <stdlib.h>
19#include <stdio.h>
20
21#include <usb/drivers/mouse.h>
22#include "../services.h"
23#include "hid.h"
24
25struct usb_mouse_device {
26	struct usb_dev *udev;
27	struct usb_hid_device *hid;
28	struct endpoint *ep_int;
29	struct xact int_xact;
30	struct mouse_event event;
31};
32
33static ssize_t mouse_read(ps_chardevice_t* d, void* vdata, size_t bytes,
34		chardev_callback_t cb, void* token)
35{
36	int size;
37	struct usb_mouse_device *mouse;
38	mouse = (struct usb_mouse_device*)d->vaddr;
39
40	size = sizeof(struct mouse_event);
41	if (size > bytes) {
42		return 0;
43	}
44
45	memcpy((uint8_t*)vdata, &mouse->event, size);
46
47	return size;
48}
49
50static int mouse_irq_handler(void* token, enum usb_xact_status stat, int bytes_remaining)
51{
52	struct usb_mouse_device *mouse;
53	uint8_t *data;
54
55	mouse = (struct usb_mouse_device*)token;
56	data = xact_get_vaddr(&mouse->int_xact);
57
58	if (stat != XACTSTAT_SUCCESS) {
59		ZF_LOGD("Received unsuccessful IRQ\n");
60	}
61
62	mouse->event.button = data[0];
63	mouse->event.x = data[1];
64	mouse->event.y = data[2];
65
66	usbdev_schedule_xact(mouse->udev, mouse->ep_int, &mouse->int_xact, 1,
67			&mouse_irq_handler, mouse);
68
69	return 0;
70}
71
72int usb_mouse_driver_bind(struct usb_dev *usb_dev, struct ps_chardevice *cdev)
73{
74	struct usb_mouse_device *mouse;
75	int err;
76
77	mouse = (struct usb_mouse_device*)usb_malloc(sizeof(struct usb_mouse_device));
78	if (!mouse) {
79		ZF_LOGF("Out of memory\n");
80	}
81
82	usb_dev->dev_data = (struct udev_priv*)mouse;
83	mouse->udev = usb_dev;
84
85	mouse->hid = usb_hid_alloc(usb_dev);
86
87	if (mouse->hid->protocol != 2) {
88		ZF_LOGF("Not a mouse: %d\n", mouse->hid->protocol);
89	}
90
91	cdev->vaddr = mouse;
92	cdev->read = mouse_read;
93
94	/* Find endpoint */
95	mouse->ep_int = usb_dev->ep[mouse->hid->iface];
96	if (mouse->ep_int == NULL || mouse->ep_int->type != EP_INTERRUPT) {
97		ZF_LOGF("Endpoint not found\n");
98	}
99
100	ZF_LOGD("Configuring mouse\n");
101
102	err = usb_hid_set_idle(mouse->hid, 0);
103	if (err < 0) {
104		ZF_LOGF("Mouse initialisation error\n");
105	}
106
107	/* Initialise IRQs */
108	if (mouse->ep_int->dir == EP_DIR_IN) {
109		mouse->int_xact.type = PID_IN;
110	} else {
111		mouse->int_xact.type = PID_OUT;
112	}
113
114	mouse->int_xact.len = mouse->ep_int->max_pkt;
115
116	err = usb_alloc_xact(usb_dev->dman, &mouse->int_xact, 1);
117	if (err) {
118		ZF_LOGF("Out of DMA memory\n");
119	}
120
121	usbdev_schedule_xact(usb_dev, mouse->ep_int, &mouse->int_xact, 1,
122			&mouse_irq_handler, mouse);
123
124	ZF_LOGD("Successfully initialised\n");
125
126	return 0;
127}
128
129