8
8
import struct
9
9
from micropython import const
10
10
11
- from .device import USBInterface , get_usbdevice
11
+ from .device import USBInterface , get
12
12
from .utils import (
13
13
Buffer ,
14
14
split_bmRequestType ,
87
87
_MP_STREAM_POLL_HUP = const (0x10 )
88
88
89
89
90
- class CDC (io .IOBase ):
90
+ class CDC (io .IOBase , USBInterface ):
91
91
# USB CDC serial device class, designed to resemble machine.UART
92
92
# with some additional methods.
93
93
#
94
- # This is a standalone class, instead of a USBInterface subclass, because
95
- # CDC consists of multiple interfaces (CDC control and CDC data) and also
96
- # so it can derive from io.IOBase.
94
+ # Relies on multiple inheritance so it can be an io.IOBase for stream
95
+ # functions and also a USBInterface (actually an Interface Association
96
+ # Descriptor holding two interfaces.)
97
97
def __init__ (self , ** kwargs ):
98
+ # io.IOBase has no __init__()
99
+ USBInterface .__init__ (self )
100
+
98
101
# For CDC to work, the device class must be set to Interface Association
99
- usb_device = get_usbdevice ()
102
+ # TODO: Fix this hack by adding a callback from _USBDevice.init() that lets
103
+ # all the USBInterface instances tweak the device class if needed
104
+ usb_device = get ()
100
105
usb_device .device_class = _DEV_CLASS_MISC
101
106
usb_device .device_subclass = 2
102
107
usb_device .device_protocol = 1 # Itf association descriptor
103
108
104
- self ._ctrl = CDCControlInterface ()
105
- self ._data = CDCDataInterface ()
106
- # The data interface *must* be added immediately after the control interface
107
- usb_device .add_interface (self ._ctrl )
108
- usb_device .add_interface (self ._data )
109
+ # Callbacks for particular control changes initiated by the host
110
+ self .break_cb = None # Host sent a "break" condition
111
+ self .line_state_cb = None
112
+ self .line_coding_cb = None
113
+
114
+ self ._line_state = 0 # DTR & RTS
115
+ # Set a default line coding of 115200/8N1
116
+ self ._line_coding = bytearray (b"\x00 \xc2 \x01 \x00 \x00 \x00 \x08 " )
117
+
118
+ self ._wb = () # Optional write Buffer (IN endpoint), set by CDC.init()
119
+ self ._rb = () # Optional read Buffer (OUT endpoint), set by CDC.init()
120
+ self ._timeout = 1000 # set from CDC.init() as well
121
+
122
+ # one control interface endpoint, two data interface endpoints
123
+ self .ep_c_in = self .ep_d_in = self .ep_d_out = None
124
+
125
+ self ._c_itf = None # Number of control interface, data interface is one more
109
126
110
127
self .init (** kwargs )
111
128
@@ -121,7 +138,7 @@ def init(
121
138
# code, the USB host sets them.)
122
139
struct .pack_into (
123
140
"<LBBB" ,
124
- self ._ctrl . _line_coding ,
141
+ self ._line_coding ,
125
142
0 ,
126
143
baudrate ,
127
144
_STOP_BITS_REPR .index (str (stop )),
@@ -135,44 +152,41 @@ def init(
135
152
if not (txbuf and rxbuf ):
136
153
raise ValueError # Buffer sizes are required
137
154
138
- self ._data ._timeout = timeout
139
- self ._data ._wb = Buffer (txbuf )
140
- self ._data ._rb = Buffer (rxbuf )
141
-
142
- def is_open (self ):
143
- return self ._ctrl .is_open ()
155
+ self ._timeout = timeout
156
+ self ._wb = Buffer (txbuf )
157
+ self ._rb = Buffer (rxbuf )
144
158
145
159
###
146
160
### Line State & Line Coding State property getters
147
161
###
148
162
149
163
@property
150
164
def rts (self ):
151
- return bool (self ._ctrl . _line_state & _LINE_STATE_RTS )
165
+ return bool (self ._line_state & _LINE_STATE_RTS )
152
166
153
167
@property
154
168
def dtr (self ):
155
- return bool (self ._ctrl . _line_state & _LINE_STATE_DTR )
169
+ return bool (self ._line_state & _LINE_STATE_DTR )
156
170
157
171
# Line Coding Representation
158
172
# Byte 0-3 Byte 4 Byte 5 Byte 6
159
173
# dwDTERate bCharFormat bParityType bDataBits
160
174
161
175
@property
162
176
def baudrate (self ):
163
- return struct .unpack ("<LBBB" , self ._ctrl . _line_coding )[0 ]
177
+ return struct .unpack ("<LBBB" , self ._line_coding )[0 ]
164
178
165
179
@property
166
180
def stop_bits (self ):
167
- return _STOP_BITS_REPR [self ._ctrl . _line_coding [4 ]]
181
+ return _STOP_BITS_REPR [self ._line_coding [4 ]]
168
182
169
183
@property
170
184
def parity (self ):
171
- return _PARITY_BITS_REPR [self ._ctrl . _line_coding [5 ]]
185
+ return _PARITY_BITS_REPR [self ._line_coding [5 ]]
172
186
173
187
@property
174
188
def data_bits (self ):
175
- return self ._ctrl . _line_coding [6 ]
189
+ return self ._line_coding [6 ]
176
190
177
191
def __repr__ (self ):
178
192
return f"{ self .baudrate } /{ self .data_bits } { self .parity } { self .stop_bits } rts={ self .rts } dtr={ self .dtr } "
@@ -182,60 +196,24 @@ def __repr__(self):
182
196
###
183
197
184
198
def set_break_cb (self , cb ):
185
- self ._ctrl . break_cb = cb
199
+ self .break_cb = cb
186
200
187
201
def set_line_state_cb (self , cb ):
188
- self ._ctrl . line_state_cb = cb
202
+ self .line_state_cb = cb
189
203
190
204
def set_line_coding_cb (self , cb ):
191
- self ._ctrl . line_coding_cb = cb
205
+ self .line_coding_cb = cb
192
206
193
207
###
194
- ### io.IOBase stream implementation
208
+ ### USB Interface Implementation
195
209
###
196
210
197
- def read (self , size = - 1 ):
198
- return self ._data .read (size )
199
-
200
- def readinto (self , b ):
201
- return self ._data .readinto (b )
202
-
203
- def write (self , buf ):
204
- return self ._data .write (buf )
205
-
206
- def flush (self ):
207
- # a C implementation of this exists in stream.c, but it's not in io.IOBase
208
- # and can't immediately be called from here (AFAIK)
209
- r = self .ioctl (_MP_STREAM_FLUSH , 0 )
210
- if r :
211
- raise OSError (r )
212
-
213
- def ioctl (self , req , arg ):
214
- return self ._data .ioctl (req , arg )
215
-
216
-
217
- class CDCControlInterface (USBInterface ):
218
- # Implements the CDC Control Interface
219
-
220
- def __init__ (self ):
221
- super ().__init__ ()
222
-
223
- # Callbacks for particular changes initiated by the host
224
- self .break_cb = None # Host sent a "break" condition
225
- self .line_state_cb = None
226
- self .line_coding_cb = None
227
-
228
- self ._line_state = 0 # DTR & RTS
229
- # Set a default line coding of 115200/8N1
230
- self ._line_coding = bytearray (b"\x00 \xc2 \x01 \x00 \x00 \x00 \x08 " )
231
-
232
- self .ep_in = None # Set when enumeration happens
233
-
234
- def descriptor_config_cb (self , desc , itf_num , ep_num ):
235
- # CDC needs a Interface Association Descriptor (IAD) connecting the Control & Data interfaces
211
+ def descriptor_config (self , desc , itf_num , ep_num , strs ):
212
+ # CDC needs a Interface Association Descriptor (IAD) wrapping two interfaces: Control & Data interfaces
236
213
desc .interface_assoc (itf_num , 2 , _INTERFACE_CLASS_CDC , _INTERFACE_SUBCLASS_CDC )
237
214
238
215
# Now add the Control interface descriptor
216
+ self ._c_itf = itf_num
239
217
desc .interface (itf_num , _CDC_CONTROL_EP_NUM , _INTERFACE_CLASS_CDC , _INTERFACE_SUBCLASS_CDC )
240
218
241
219
# Append the CDC class-specific interface descriptor
@@ -280,14 +258,46 @@ def descriptor_config_cb(self, desc, itf_num, ep_num):
280
258
itf_num + 1 , # bSubordinateInterface0 (data class itf number)
281
259
)
282
260
283
- # Descriptor for a single endpoint
284
- self .ep_in = ep_num | EP_IN_FLAG
285
- desc .endpoint (self .ep_in , "interrupt" , 8 , 16 )
261
+ # Single control IN endpoint (currently unused in this implementation)
262
+ self .ep_c_in = ep_num | EP_IN_FLAG
263
+ desc .endpoint (self .ep_c_in , "interrupt" , 8 , 16 )
264
+
265
+ # Now add the data interface
266
+ desc .interface (
267
+ itf_num + 1 ,
268
+ _CDC_DATA_EP_NUM ,
269
+ _CDC_ITF_DATA_CLASS ,
270
+ _CDC_ITF_DATA_SUBCLASS ,
271
+ _CDC_ITF_DATA_PROT ,
272
+ )
273
+
274
+ # Two data endpoints, bulk OUT and IN
275
+ self .ep_d_out = ep_num + 1
276
+ self .ep_d_in = (ep_num + 1 ) | EP_IN_FLAG
277
+ desc .endpoint (self .ep_d_out , "bulk" , _BULK_EP_LEN , 0 )
278
+ desc .endpoint (self .ep_d_in , "bulk" , _BULK_EP_LEN , 0 )
279
+
280
+ def num_itfs (self ):
281
+ return 2
282
+
283
+ def num_eps (self ):
284
+ return 2 # total after masking out EP_IN_FLAG
285
+
286
+ def handle_open (self ):
287
+ super ().handle_open ()
288
+ # kick off any transfers that may have queued while the device was not open
289
+ self ._rd_xfer ()
290
+ self ._wr_xfer ()
286
291
287
292
def handle_interface_control_xfer (self , stage , request ):
288
293
# Handle class-specific interface control transfers
289
- bmRequestType , bRequest , wValue , _ , wLength = struct .unpack ("BBHHH" , request )
294
+ bmRequestType , bRequest , wValue , wIndex , wLength = struct .unpack ("BBHHH" , request )
290
295
recipient , req_type , req_dir = split_bmRequestType (bmRequestType )
296
+
297
+ # Only for the control interface
298
+ if wIndex != self ._c_itf :
299
+ return False
300
+
291
301
if stage == STAGE_SETUP :
292
302
if req_type == REQ_TYPE_CLASS :
293
303
if bRequest == _SET_LINE_CODING_REQ :
@@ -317,34 +327,34 @@ def handle_interface_control_xfer(self, stage, request):
317
327
318
328
return True
319
329
330
+ def _wr_xfer (self ):
331
+ # Submit a new data IN transfer from the _wb buffer, if needed
332
+ if self .is_open () and not self .xfer_pending (self .ep_d_in ) and self ._wb .readable ():
333
+ self .submit_xfer (self .ep_d_in , self ._wb .pend_read (), self ._wr_cb )
320
334
321
- class CDCDataInterface (USBInterface ):
322
- # Implements the CDC Data Interface
323
-
324
- def __init__ (self ):
325
- super ().__init__ ()
326
-
327
- self ._wb = () # Optional write Buffer (IN endpoint), set by CDC.init()
328
- self ._rb = () # Optional read Buffer (OUT endpoint), set by CDC.init()
329
- self ._timeout = 1000 # set from CDC.init() as well
335
+ def _wr_cb (self , ep , res , num_bytes ):
336
+ # Whenever a data IN transfer ends
337
+ if res == 0 :
338
+ self ._wb .finish_read (num_bytes )
339
+ self ._wr_xfer ()
330
340
331
- self .ep_in = self .ep_out = None # Set when enumeration happens
341
+ def _rd_xfer (self ):
342
+ # Keep an active data OUT transfer to read data from the host,
343
+ # whenever the receive buffer has room for new data
344
+ if self .is_open () and not self .xfer_pending (self .ep_d_out ) and self ._rb .writable ():
345
+ # Can only submit up to the endpoint length per transaction, otherwise we won't
346
+ # get any transfer callback until the full transaction completes.
347
+ self .submit_xfer (self .ep_d_out , self ._rb .pend_write (_BULK_EP_LEN ), self ._rd_cb )
332
348
333
- def descriptor_config_cb (self , desc , itf_num , ep_num ):
334
- # Add the standard interface descriptor
335
- desc .interface (
336
- itf_num ,
337
- _CDC_DATA_EP_NUM ,
338
- _CDC_ITF_DATA_CLASS ,
339
- _CDC_ITF_DATA_SUBCLASS ,
340
- _CDC_ITF_DATA_PROT ,
341
- )
349
+ def _rd_cb (self , ep , res , num_bytes ):
350
+ # Whenever a data OUT transfer ends
351
+ if res == 0 :
352
+ self ._rb .finish_write (num_bytes )
353
+ self ._rd_xfer ()
342
354
343
- # Two endpoints, bulk OUT and IN
344
- self .ep_out = ep_num
345
- self .ep_in = ep_num | EP_IN_FLAG
346
- desc .endpoint (self .ep_out , "bulk" , _BULK_EP_LEN , 0 )
347
- desc .endpoint (self .ep_in , "bulk" , _BULK_EP_LEN , 0 )
355
+ ###
356
+ ### io.IOBase stream implementation
357
+ ###
348
358
349
359
def write (self , buf ):
350
360
# use a memoryview to track how much of 'buf' we've written so far
@@ -365,36 +375,6 @@ def write(self, buf):
365
375
if time .ticks_diff (time .ticks_ms (), start ) > self ._timeout :
366
376
return len (buf ) - len (mv )
367
377
368
- def _wr_xfer (self ):
369
- # Submit a new IN transfer from the _wb buffer, if needed
370
- if self .is_open () and not self .xfer_pending (self .ep_in ) and self ._wb .readable ():
371
- self .submit_xfer (self .ep_in , self ._wb .pend_read (), self ._wr_cb )
372
-
373
- def _wr_cb (self , ep , res , num_bytes ):
374
- # Whenever an IN transfer ends
375
- if res == 0 :
376
- self ._wb .finish_read (num_bytes )
377
- self ._wr_xfer ()
378
-
379
- def _rd_xfer (self ):
380
- # Keep an active OUT transfer to read data from the host,
381
- # whenever the receive buffer has room for new data
382
- if self .is_open () and not self .xfer_pending (self .ep_out ) and self ._rb .writable ():
383
- # Can only submit up to the endpoint length per transaction, otherwise we won't
384
- # get any transfer callback until the full transaction completes.
385
- self .submit_xfer (self .ep_out , self ._rb .pend_write (_BULK_EP_LEN ), self ._rd_cb )
386
-
387
- def _rd_cb (self , ep , res , num_bytes ):
388
- if res == 0 :
389
- self ._rb .finish_write (num_bytes )
390
- self ._rd_xfer ()
391
-
392
- def handle_open (self ):
393
- super ().handle_open ()
394
- # kick off any transfers that may have queued while the device was not open
395
- self ._rd_xfer ()
396
- self ._wr_xfer ()
397
-
398
378
def read (self , size ):
399
379
start = time .ticks_ms ()
400
380
@@ -457,3 +437,10 @@ def ioctl(self, req, arg):
457
437
return 0
458
438
459
439
return _MP_EINVAL
440
+
441
+ def flush (self ):
442
+ # a C implementation of this exists in stream.c, but it's not in io.IOBase
443
+ # and can't immediately be called from here (AFAIK)
444
+ r = self .ioctl (_MP_STREAM_FLUSH , 0 )
445
+ if r :
446
+ raise OSError (r )
0 commit comments