1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.accessorydisplay.sink;
18
19import java.nio.ByteBuffer;
20
21/**
22 * Helper for creating USB HID descriptors and reports.
23 */
24final class UsbHid {
25    private UsbHid() {
26    }
27
28    /**
29     * Generates basic Windows 7 compatible HID multitouch descriptors and reports
30     * that should be supported by recent versions of the Linux hid-multitouch driver.
31     */
32    public static final class Multitouch {
33        private final int mReportId;
34        private final int mMaxContacts;
35        private final int mWidth;
36        private final int mHeight;
37
38        public Multitouch(int reportId, int maxContacts, int width, int height) {
39            mReportId = reportId;
40            mMaxContacts = maxContacts;
41            mWidth = width;
42            mHeight = height;
43        }
44
45        public void generateDescriptor(ByteBuffer buffer) {
46            buffer.put(new byte[] {
47                0x05, 0x0d,                         // USAGE_PAGE (Digitizers)
48                0x09, 0x04,                         // USAGE (Touch Screen)
49                (byte)0xa1, 0x01,                   // COLLECTION (Application)
50                (byte)0x85, (byte)mReportId,        //   REPORT_ID (Touch)
51                0x09, 0x22,                         //   USAGE (Finger)
52                (byte)0xa1, 0x00,                   //   COLLECTION (Physical)
53                0x09, 0x55,                         //     USAGE (Contact Count Maximum)
54                0x15, 0x00,                         //     LOGICAL_MINIMUM (0)
55                0x25, (byte)mMaxContacts,           //     LOGICAL_MAXIMUM (...)
56                0x75, 0x08,                         //     REPORT_SIZE (8)
57                (byte)0x95, 0x01,                   //     REPORT_COUNT (1)
58                (byte)0xb1, (byte)mMaxContacts,     //     FEATURE (Data,Var,Abs)
59                0x09, 0x54,                         //     USAGE (Contact Count)
60                (byte)0x81, 0x02,                   //     INPUT (Data,Var,Abs)
61            });
62            byte maxXLsb = (byte)(mWidth - 1);
63            byte maxXMsb = (byte)((mWidth - 1) >> 8);
64            byte maxYLsb = (byte)(mHeight - 1);
65            byte maxYMsb = (byte)((mHeight - 1) >> 8);
66            byte[] collection = new byte[] {
67                0x05, 0x0d,                         //     USAGE_PAGE (Digitizers)
68                0x09, 0x22,                         //     USAGE (Finger)
69                (byte)0xa1, 0x02,                   //     COLLECTION (Logical)
70                0x09, 0x42,                         //       USAGE (Tip Switch)
71                0x15, 0x00,                         //       LOGICAL_MINIMUM (0)
72                0x25, 0x01,                         //       LOGICAL_MAXIMUM (1)
73                0x75, 0x01,                         //       REPORT_SIZE (1)
74                (byte)0x81, 0x02,                   //       INPUT (Data,Var,Abs)
75                0x09, 0x32,                         //       USAGE (In Range)
76                (byte)0x81, 0x02,                   //       INPUT (Data,Var,Abs)
77                0x09, 0x51,                         //       USAGE (Contact Identifier)
78                0x25, 0x3f,                         //       LOGICAL_MAXIMUM (63)
79                0x75, 0x06,                         //       REPORT_SIZE (6)
80                (byte)0x81, 0x02,                   //       INPUT (Data,Var,Abs)
81                0x05, 0x01,                         //       USAGE_PAGE (Generic Desktop)
82                0x09, 0x30,                         //       USAGE (X)
83                0x26, maxXLsb, maxXMsb,             //       LOGICAL_MAXIMUM (...)
84                0x75, 0x10,                         //       REPORT_SIZE (16)
85                (byte)0x81, 0x02,                   //       INPUT (Data,Var,Abs)
86                0x09, 0x31,                         //       USAGE (Y)
87                0x26, maxYLsb, maxYMsb,             //       LOGICAL_MAXIMUM (...)
88                (byte)0x81, 0x02,                   //       INPUT (Data,Var,Abs)
89                (byte)0xc0,                         //     END_COLLECTION
90            };
91            for (int i = 0; i < mMaxContacts; i++) {
92                buffer.put(collection);
93            }
94            buffer.put(new byte[] {
95                (byte)0xc0,                         //   END_COLLECTION
96                (byte)0xc0,                         // END_COLLECTION
97            });
98        }
99
100        public void generateReport(ByteBuffer buffer, Contact[] contacts, int contactCount) {
101            // Report Id
102            buffer.put((byte)mReportId);
103            // Contact Count
104            buffer.put((byte)contactCount);
105
106            for (int i = 0; i < contactCount; i++) {
107                final Contact contact = contacts[i];
108                // Tip Switch, In Range, Contact Identifier
109                buffer.put((byte)((contact.id << 2) | 0x03));
110                // X
111                buffer.put((byte)contact.x).put((byte)(contact.x >> 8));
112                // Y
113                buffer.put((byte)contact.y).put((byte)(contact.y >> 8));
114            }
115            for (int i = contactCount; i < mMaxContacts; i++) {
116                buffer.put((byte)0).put((byte)0).put((byte)0).put((byte)0).put((byte)0);
117            }
118        }
119
120        public int getReportSize() {
121            return 2 + mMaxContacts * 5;
122        }
123
124        public static final class Contact {
125            public int id; // range 0..63
126            public int x;
127            public int y;
128        }
129    }
130}
131