summaryrefslogtreecommitdiff
path: root/UsbDk/UsbTarget.h
blob: 44ac609a4d3794c65cb25076ac3940ea0a027411 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/**********************************************************************
* Copyright (c) 2013-2014  Red Hat, Inc.
*
* Developed by Daynix Computing LTD.
*
* Authors:
*     Dmitry Fleytman <dmitry@daynix.com>
*     Pavel Gurvich <pavel@daynix.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**********************************************************************/

#pragma once

#include "Alloc.h"
#include "UsbDkUtil.h"
#include "Urb.h"
#include "WdfRequest.h"

using USBDK_TARGET_REQUEST_CONTEXT = struct : public WDF_REQUEST_CONTEXT
{
    ULONG64 RequestId;
};
using PUSBDK_TARGET_REQUEST_CONTEXT = USBDK_TARGET_REQUEST_CONTEXT*;

class CTargetRequest : public CWdfRequest
{
public:
    explicit CTargetRequest(WDFREQUEST Request)
        : CWdfRequest(Request)
    {}

    PUSBDK_TARGET_REQUEST_CONTEXT Context() const
    { return static_cast<PUSBDK_TARGET_REQUEST_CONTEXT>(CWdfRequest::Context()); }

    void SetId(ULONG64 Id) const
    { Context()->RequestId = Id; }

    ULONG64 GetId() const
    { return Context()->RequestId; }
};

class CWdfUsbPipe : public CAllocatable<NonPagedPool, 'PUHR'>
{
public:
    CWdfUsbPipe()
    {}

    void Create(WDFUSBDEVICE Device, WDFUSBINTERFACE Interface, UCHAR PipeIndex);
    void ReadAsync(CTargetRequest &Request, WDFMEMORY Buffer, PFN_WDF_REQUEST_COMPLETION_ROUTINE Completion);
    void WriteAsync(CTargetRequest &Request, WDFMEMORY Buffer, PFN_WDF_REQUEST_COMPLETION_ROUTINE Completion);

    void ReadIsochronousAsync(CTargetRequest &Request,
        WDFMEMORY Buffer,
        PULONG64 PacketSizes,
        size_t PacketNumber,
        PFN_WDF_REQUEST_COMPLETION_ROUTINE Completion)
    {
        SubmitIsochronousTransfer(Request, CIsochronousUrb::URB_DIRECTION_IN, Buffer, PacketSizes, PacketNumber, Completion);
    }

    void WriteIsochronousAsync(CTargetRequest &Request,
        WDFMEMORY Buffer,
        PULONG64 PacketSizes,
        size_t PacketNumber,
        PFN_WDF_REQUEST_COMPLETION_ROUTINE Completion)
    {
        SubmitIsochronousTransfer(Request, CIsochronousUrb::URB_DIRECTION_OUT, Buffer, PacketSizes, PacketNumber, Completion);
    }

    NTSTATUS Abort(WDFREQUEST Request);
    NTSTATUS Reset(WDFREQUEST Request);
    UCHAR EndpointAddress() const
    {
        return m_Info.EndpointAddress;
    }

private:

    WDFUSBINTERFACE m_Interface = WDF_NO_HANDLE;
    WDFUSBDEVICE m_Device = WDF_NO_HANDLE;
    WDFUSBPIPE m_Pipe = WDF_NO_HANDLE;
    WDF_USB_PIPE_INFORMATION m_Info;
    CAtomicCounter m_RequestConter;

    void SubmitIsochronousTransfer(CTargetRequest &Request,
        CIsochronousUrb::Direction Direction,
        WDFMEMORY Buffer,
        PULONG64 PacketSizes,
        size_t PacketNumber,
        PFN_WDF_REQUEST_COMPLETION_ROUTINE Completion);

    CWdfUsbPipe(const CWdfUsbPipe&) = delete;
    CWdfUsbPipe& operator= (const CWdfUsbPipe&) = delete;
};

class CWdfUsbInterface : public CAllocatable<NonPagedPool, 'IUHR'>
{
public:
    CWdfUsbInterface()
    {}

    NTSTATUS Create(WDFUSBDEVICE Device, UCHAR InterfaceIdx);
    NTSTATUS SetAltSetting(ULONG64 AltSettingIdx);

    template<typename TLockingStrategy, typename TFunctor>
    bool DoPipeOperation(ULONG64 EndpointAddress, TFunctor Functor)
    {
        TLockingStrategy m_LockedContext(m_PipesLock);

        for (UCHAR i = 0; i < m_NumPipes; i++)
        {
            if (m_Pipes[i].EndpointAddress() == EndpointAddress)
            {
                Functor(m_Pipes[i]);
                return true;
            }
        }

        return false;
    }

    NTSTATUS Reset(WDFREQUEST Request);

    class Lock : public CWdmExSpinLock
    {
    public:
        void NoLock() {};
        void LockShared() { CWdmExSpinLock::LockShared(); }
        void UnlockShared() { CWdmExSpinLock::UnlockShared(); }
        void LockExclusive() { CWdmExSpinLock::LockExclusive(); }
        void UnlockExclusive() { CWdmExSpinLock::UnlockExclusive(); }
    };

    using SharedLock = CBaseLockedContext < Lock, &Lock::LockShared, &Lock::UnlockShared >;
    using ExclusiveLock = CBaseLockedContext < Lock, &Lock::LockExclusive, &Lock::UnlockExclusive > ;
    using NeitherLock = CBaseLockedContext < Lock, &Lock::NoLock, &Lock::NoLock >;

private:
    WDFUSBDEVICE m_UsbDevice;
    WDFUSBINTERFACE m_Interface;

    Lock m_PipesLock;
    CObjHolder<CWdfUsbPipe, CVectorDeleter<CWdfUsbPipe> > m_Pipes;
    BYTE m_NumPipes = 0;

    CWdfUsbInterface(const CWdfUsbInterface&) = delete;
    CWdfUsbInterface& operator= (const CWdfUsbInterface&) = delete;
};

class CWdfUsbTarget
{
public:
    CWdfUsbTarget() {}

    NTSTATUS Create(WDFDEVICE Device);
    void DeviceDescriptor(USB_DEVICE_DESCRIPTOR &Descriptor);
    NTSTATUS SetInterfaceAltSetting(ULONG64 InterfaceIdx, ULONG64 AltSettingIdx);

    void WritePipeAsync(WDFREQUEST Request, ULONG64 EndpointAddress, WDFMEMORY Buffer, PFN_WDF_REQUEST_COMPLETION_ROUTINE Completion);
    void ReadPipeAsync(WDFREQUEST Request, ULONG64 EndpointAddress, WDFMEMORY Buffer, PFN_WDF_REQUEST_COMPLETION_ROUTINE Completion);

    void ReadIsochronousPipeAsync(WDFREQUEST Request, ULONG64 EndpointAddress, WDFMEMORY Buffer,
                                  PULONG64 PacketSizes, size_t PacketNumber,
                                  PFN_WDF_REQUEST_COMPLETION_ROUTINE Completion);
    void WriteIsochronousPipeAsync(WDFREQUEST Request, ULONG64 EndpointAddress, WDFMEMORY Buffer,
                                   PULONG64 PacketSizes, size_t PacketNumber,
                                   PFN_WDF_REQUEST_COMPLETION_ROUTINE Completion);

    NTSTATUS ControlTransferAsync(CTargetRequest &WdfRequest, PWDF_USB_CONTROL_SETUP_PACKET SetupPacket, WDFMEMORY Data,
                                  PWDFMEMORY_OFFSET TransferOffset, PFN_WDF_REQUEST_COMPLETION_ROUTINE Completion);
    NTSTATUS AbortPipe(WDFREQUEST Request, ULONG64 EndpointAddress);
    NTSTATUS ResetPipe(WDFREQUEST Request, ULONG64 EndpointAddress);
    NTSTATUS ResetDevice(WDFREQUEST Request);

private:
    void TracePipeNotFoundError(ULONG64 EndpointAddress);

    template<typename TLockingStrategy, typename TFunctor>
    bool DoPipeOperation(ULONG64 EndpointAddress, TFunctor Functor)
    {
        for (UCHAR i = 0; i < m_NumInterfaces; i++)
        {
            if (m_Interfaces[i].DoPipeOperation<TLockingStrategy, TFunctor>(EndpointAddress, Functor))
            {
                return true;
            }
        }

        TracePipeNotFoundError(EndpointAddress);
        return false;
    }

    WDFDEVICE m_Device = WDF_NO_HANDLE;
    WDFUSBDEVICE m_UsbDevice = WDF_NO_HANDLE;

    CObjHolder<CWdfUsbInterface, CVectorDeleter<CWdfUsbInterface> > m_Interfaces;
    UCHAR m_NumInterfaces = 0;

    CAtomicCounter m_ControlTransferCouter;

    CWdfUsbTarget(const CWdfUsbTarget&) = delete;
    CWdfUsbTarget& operator= (const CWdfUsbTarget&) = delete;
};