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 
17 package com.android.accessorydisplay.sink;
18 
19 import java.nio.ByteBuffer;
20 
21 /**
22  * Helper for creating USB HID descriptors and reports.
23  */
24 final class UsbHid {
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 
Multitouch(int reportId, int maxContacts, int width, int height)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 
generateDescriptor(ByteBuffer buffer)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 
generateReport(ByteBuffer buffer, Contact[] contacts, int contactCount)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 
getReportSize()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