summaryrefslogtreecommitdiffstats
path: root/lib/usbhost/USB_Host_Shield_2.0/address.h
blob: c3e1b3141f2741f83c8d9310bc4a17376fe3eded (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
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.

This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").

Contact information
-------------------

Circuits At Home, LTD
Web      :  http://www.circuitsathome.com
e-mail   :  support@circuitsathome.com
 */

#if !defined(_usb_h_) || defined(__ADDRESS_H__)
#error "Never include address.h directly; include Usb.h instead"
#else
#define __ADDRESS_H__



/* NAK powers. To save space in endpoint data structure, amount of retries before giving up and returning 0x4 is stored in */
/* bmNakPower as a power of 2. The actual nak_limit is then calculated as nak_limit = ( 2^bmNakPower - 1) */
#define USB_NAK_MAX_POWER               15              //NAK binary order maximum value
#define USB_NAK_DEFAULT                 14              //default 32K-1 NAKs before giving up
#define USB_NAK_NOWAIT                  1               //Single NAK stops transfer
#define USB_NAK_NONAK                   0               //Do not count NAKs, stop retrying after USB Timeout

struct EpInfo {
        uint8_t epAddr; // Endpoint address
        uint8_t maxPktSize; // Maximum packet size

        union {
                uint8_t epAttribs;

                struct {
                        uint8_t bmSndToggle : 1; // Send toggle, when zero bmSNDTOG0, bmSNDTOG1 otherwise
                        uint8_t bmRcvToggle : 1; // Send toggle, when zero bmRCVTOG0, bmRCVTOG1 otherwise
                        uint8_t bmNakPower : 6; // Binary order for NAK_LIMIT value
                } __attribute__((packed));
        };
} __attribute__((packed));

//        7   6   5   4   3   2   1   0
//  ---------------------------------
//  |   | H | P | P | P | A | A | A |
//  ---------------------------------
//
// H - if 1 the address is a hub address
// P - parent hub address
// A - device address / port number in case of hub
//

struct UsbDeviceAddress {

        union {

                struct {
                        uint8_t bmAddress : 3; // device address/port number
                        uint8_t bmParent : 3; // parent hub address
                        uint8_t bmHub : 1; // hub flag
                        uint8_t bmReserved : 1; // reserved, must be zero
                } __attribute__((packed));
                uint8_t devAddress;
        };
} __attribute__((packed));

#define bmUSB_DEV_ADDR_ADDRESS          0x07
#define bmUSB_DEV_ADDR_PARENT           0x38
#define bmUSB_DEV_ADDR_HUB              0x40

struct UsbDevice {
        EpInfo *epinfo; // endpoint info pointer
        UsbDeviceAddress address;
        uint8_t epcount; // number of endpoints
        bool lowspeed; // indicates if a device is the low speed one
        //      uint8_t devclass; // device class
} __attribute__((packed));

class AddressPool {
public:
        virtual UsbDevice* GetUsbDevicePtr(uint8_t addr) = 0;
        virtual uint8_t AllocAddress(uint8_t parent, bool is_hub = false, uint8_t port = 0) = 0;
        virtual void FreeAddress(uint8_t addr) = 0;
};

typedef void (*UsbDeviceHandleFunc)(UsbDevice *pdev);

#define ADDR_ERROR_INVALID_INDEX                0xFF
#define ADDR_ERROR_INVALID_ADDRESS              0xFF

template <const uint8_t MAX_DEVICES_ALLOWED>
class AddressPoolImpl : public AddressPool {
        EpInfo dev0ep; //Endpoint data structure used during enumeration for uninitialized device

        uint8_t hubCounter; // hub counter is kept
        // in order to avoid hub address duplication

        UsbDevice thePool[MAX_DEVICES_ALLOWED];

        // Initializes address pool entry

        void InitEntry(uint8_t index) {
                thePool[index].address.devAddress = 0;
                thePool[index].epcount = 1;
                thePool[index].lowspeed = 0;
                thePool[index].epinfo = &dev0ep;
        };

        // Returns thePool index for a given address

        uint8_t FindAddressIndex(uint8_t address = 0) {
                for(uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++) {
                        if(thePool[i].address.devAddress == address)
                                return i;
                }
                return 0;
        };

        // Returns thePool child index for a given parent

        uint8_t FindChildIndex(UsbDeviceAddress addr, uint8_t start = 1) {
                for(uint8_t i = (start < 1 || start >= MAX_DEVICES_ALLOWED) ? 1 : start; i < MAX_DEVICES_ALLOWED; i++) {
                        if(thePool[i].address.bmParent == addr.bmAddress)
                                return i;
                }
                return 0;
        };

        // Frees address entry specified by index parameter

        void FreeAddressByIndex(uint8_t index) {
                // Zero field is reserved and should not be affected
                if(index == 0)
                        return;

                UsbDeviceAddress uda = thePool[index].address;
                // If a hub was switched off all port addresses should be freed
                if(uda.bmHub == 1) {
                        for(uint8_t i = 1; (i = FindChildIndex(uda, i));)
                                FreeAddressByIndex(i);

                        // If the hub had the last allocated address, hubCounter should be decremented
                        if(hubCounter == uda.bmAddress)
                                hubCounter--;
                }
                InitEntry(index);
        }

        // Initializes the whole address pool at once

        void InitAllAddresses() {
                for(uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++)
                        InitEntry(i);

                hubCounter = 0;
        };

public:

        AddressPoolImpl() : hubCounter(0) {
                // Zero address is reserved
                InitEntry(0);

                thePool[0].address.devAddress = 0;
                thePool[0].epinfo = &dev0ep;
                dev0ep.epAddr = 0;
                dev0ep.maxPktSize = 8;
                dev0ep.epAttribs = 0; //set DATA0/1 toggles to 0
                dev0ep.bmNakPower = USB_NAK_MAX_POWER;

                InitAllAddresses();
        };

        // Returns a pointer to a specified address entry

        virtual UsbDevice* GetUsbDevicePtr(uint8_t addr) {
                if(!addr)
                        return thePool;

                uint8_t index = FindAddressIndex(addr);

                return (!index) ? NULL : thePool + index;
        };

        // Performs an operation specified by pfunc for each addressed device

        void ForEachUsbDevice(UsbDeviceHandleFunc pfunc) {
                if(!pfunc)
                        return;

                for(uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++)
                        if(thePool[i].address.devAddress)
                                pfunc(thePool + i);
        };

        // Allocates new address

        virtual uint8_t AllocAddress(uint8_t parent, bool is_hub = false, uint8_t port = 0) {
                /* if (parent != 0 && port == 0)
                        USB_HOST_SERIAL.println("PRT:0"); */
                UsbDeviceAddress _parent;
                _parent.devAddress = parent;
                if(_parent.bmReserved || port > 7)
                        //if(parent > 127 || port > 7)
                        return 0;

                if(is_hub && hubCounter == 7)
                        return 0;

                // finds first empty address entry starting from one
                uint8_t index = FindAddressIndex(0);

                if(!index) // if empty entry is not found
                        return 0;

                if(_parent.devAddress == 0) {
                        if(is_hub) {
                                thePool[index].address.devAddress = 0x41;
                                hubCounter++;
                        } else
                                thePool[index].address.devAddress = 1;

                        return thePool[index].address.devAddress;
                }

                UsbDeviceAddress addr;
                addr.devAddress = 0; // Ensure all bits are zero
                addr.bmParent = _parent.bmAddress;
                if(is_hub) {
                        addr.bmHub = 1;
                        addr.bmAddress = ++hubCounter;
                } else {
                        addr.bmHub = 0;
                        addr.bmAddress = port;
                }
                thePool[index].address = addr;
                /*
                                USB_HOST_SERIAL.print("Addr:");
                                USB_HOST_SERIAL.print(addr.bmHub, HEX);
                                USB_HOST_SERIAL.print(".");
                                USB_HOST_SERIAL.print(addr.bmParent, HEX);
                                USB_HOST_SERIAL.print(".");
                                USB_HOST_SERIAL.println(addr.bmAddress, HEX);
                 */
                return thePool[index].address.devAddress;
        };

        // Empties pool entry

        virtual void FreeAddress(uint8_t addr) {
                // if the root hub is disconnected all the addresses should be initialized
                if(addr == 0x41) {
                        InitAllAddresses();
                        return;
                }
                uint8_t index = FindAddressIndex(addr);
                FreeAddressByIndex(index);
        };

        // Returns number of hubs attached
        // It can be rather helpfull to find out if there are hubs attached than getting the exact number of hubs.
        //uint8_t GetNumHubs()
        //{
        //        return hubCounter;
        //};
        //uint8_t GetNumDevices()
        //{
        //        uint8_t counter = 0;

        //        for (uint8_t i=1; i<MAX_DEVICES_ALLOWED; i++)
        //                if (thePool[i].address != 0);
        //                        counter ++;

        //        return counter;
        //};
};

#endif // __ADDRESS_H__