1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#pragma once
6
7#include <ddk/driver.h>
8#include <ddk/protocol/hidbus.h>
9#include <ddktl/protocol/hidbus-internal.h>
10#include <fbl/type_support.h>
11#include <fbl/unique_ptr.h>
12#include <zircon/assert.h>
13
14// DDK hidbus protocol support
15//
16// :: Proxies ::
17//
18// ddk::HidBusIfcProxy is simple wrappers around hidbus_ifc_t. It does not own the pointers passed
19// to it.
20//
21// :: Mixins ::
22//
23// ddk::HidBusProtocol is a mixin class that simplifies writing DDK drivers that
24// implement the hidbus protocol. They take care of implementing the function pointer tables
25// and calling into the object that wraps them.
26//
27// :: Examples ::
28//
29// // A driver that implements a ZX_PROTOCOL_HIDBUS device
30// class HidBusDevice;
31// using HidBusDeviceType = ddk::Device<HidBusDevice, /* ddk mixins */>;
32//
33// class HidBusDevice : public HidBusDeviceType,
34//                      public ddk::HidBusProtocol<HidBusDevice> {
35//   public:
36//     HidBusDevice(zx_device_t* parent)
37//       : HidBusDeviceType(parent) {}
38//
39//     zx_status_t Bind() {
40//         DdkAdd();
41//     }
42//
43//     void DdkRelease() {
44//         // Clean up
45//     }
46//
47//     zx_status_t HidBusStart(ddk::HidBusIfcProxy proxy) {
48//         // Start hidbus operation
49//         proxy_ = proxy;
50//         return ZX_OK;
51//     }
52//
53//     zx_status_t HidBusQuery(uint32_t options, hid_info_t* info) {
54//         ...
55//     }
56//
57//     ...
58//   private:
59//     ddk::HidBusIfcProxy proxy_;
60//     ...
61// };
62
63namespace ddk {
64
65class HidBusIfcProxy {
66public:
67    HidBusIfcProxy()
68        : ifc_(nullptr), cookie_(nullptr) {}
69
70    HidBusIfcProxy(hidbus_ifc_t* ifc, void* cookie)
71        : ifc_(ifc), cookie_(cookie) {}
72
73    void IoQueue(const uint8_t* buf, size_t len) {
74        ifc_->io_queue(cookie_, buf, len);
75    }
76
77    bool is_valid() const {
78        return ifc_ != nullptr;
79    }
80
81    void clear() {
82        ifc_ = nullptr;
83        cookie_ = nullptr;
84    }
85
86private:
87    hidbus_ifc_t* ifc_;
88    void* cookie_;
89};
90
91template <typename D>
92class HidBusProtocol : public internal::base_protocol {
93public:
94    HidBusProtocol() {
95        internal::CheckHidBusProtocolSubclass<D>();
96        ops_.query = Query;
97        ops_.start = Start;
98        ops_.stop = Stop;
99        ops_.get_descriptor = GetDescriptor;
100        ops_.get_report = GetReport;
101        ops_.set_report = SetReport;
102        ops_.get_idle = GetIdle;
103        ops_.set_idle = SetIdle;
104        ops_.get_protocol = GetProtocol;
105        ops_.set_protocol = SetProtocol;
106
107        // Can only inherit from one base_protocol implemenation
108        ZX_ASSERT(ddk_proto_id_ == 0);
109        ddk_proto_id_ = ZX_PROTOCOL_HIDBUS;
110        ddk_proto_ops_ = &ops_;
111    }
112
113private:
114    static zx_status_t Query(void* ctx, uint32_t options, hid_info_t* info) {
115        return static_cast<D*>(ctx)->HidBusQuery(options, info);
116    }
117
118    static zx_status_t Start(void* ctx, hidbus_ifc_t* ifc, void* cookie) {
119        HidBusIfcProxy proxy(ifc, cookie);
120        return static_cast<D*>(ctx)->HidBusStart(proxy);
121    }
122
123    static void Stop(void* ctx) {
124        static_cast<D*>(ctx)->HidBusStop();
125    }
126
127    static zx_status_t GetDescriptor(void* ctx, uint8_t desc_type, void** data, size_t* len) {
128        return static_cast<D*>(ctx)->HidBusGetDescriptor(desc_type, data, len);
129    }
130
131    static zx_status_t GetReport(void* ctx, uint8_t rpt_type, uint8_t rpt_id, void* data,
132                                 size_t len, size_t* out_len) {
133        return static_cast<D*>(ctx)->HidBusGetReport(rpt_type, rpt_id, data, len, out_len);
134    }
135
136    static zx_status_t SetReport(void* ctx, uint8_t rpt_type, uint8_t rpt_id, void* data,
137                                 size_t len) {
138        return static_cast<D*>(ctx)->HidBusSetReport(rpt_type, rpt_id, data, len);
139    }
140
141    static zx_status_t GetIdle(void* ctx, uint8_t rpt_id, uint8_t* duration) {
142        return static_cast<D*>(ctx)->HidBusGetIdle(rpt_id, duration);
143    }
144
145    static zx_status_t SetIdle(void* ctx, uint8_t rpt_id, uint8_t duration) {
146        return static_cast<D*>(ctx)->HidBusSetIdle(rpt_id, duration);
147    }
148
149    static zx_status_t GetProtocol(void* ctx, uint8_t* protocol) {
150        return static_cast<D*>(ctx)->HidBusGetProtocol(protocol);
151    }
152
153    static zx_status_t SetProtocol(void* ctx, uint8_t protocol) {
154        return static_cast<D*>(ctx)->HidBusSetProtocol(protocol);
155    }
156
157    hidbus_protocol_ops_t ops_ = {};
158};
159
160} // namespace ddk
161