Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/firmware/keyspan_pda/keyspan_pda.S
10821 views
1
/* $Id: loop.s,v 1.23 2000/03/20 09:49:06 warner Exp $
2
*
3
* Firmware for the Keyspan PDA Serial Adapter, a USB serial port based on
4
* the EzUSB microcontroller.
5
*
6
* (C) Copyright 2000 Brian Warner <[email protected]>
7
*
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation; either version 2 of the License, or
11
* (at your option) any later version.
12
*
13
* "Keyspan PDA Serial Adapter" is probably a copyright of Keyspan, the
14
* company.
15
*
16
* This serial adapter is basically an EzUSB chip and an RS-232 line driver
17
* in a little widget that has a DB-9 on one end and a USB plug on the other.
18
* It uses the EzUSB's internal UART0 (using the pins from Port C) and timer2
19
* as a baud-rate generator. The wiring is:
20
* PC0/RxD0 <- rxd (DB9 pin 2) PC4 <- dsr pin 6
21
* PC1/TxD0 -> txd pin 3 PC5 <- ri pin 9
22
* PC2 -> rts pin 7 PC6 <- dcd pin 1
23
* PC3 <- cts pin 8 PC7 -> dtr pin 4
24
* PB1 -> line driver standby
25
*
26
* The EzUSB register constants below come from their excellent documentation
27
* and sample code (which used to be available at www.anchorchips.com, but
28
* that has now been absorbed into Cypress' site and the CD-ROM contents
29
* don't appear to be available online anymore). If we get multiple
30
* EzUSB-based drivers into the kernel, it might be useful to pull them out
31
* into a separate .h file.
32
*
33
* THEORY OF OPERATION:
34
*
35
* There are two 256-byte ring buffers, one for tx, one for rx.
36
*
37
* EP2out is pure tx data. When it appears, the data is copied into the tx
38
* ring and serial transmission is started if it wasn't already running. The
39
* "tx buffer empty" interrupt may kick off another character if the ring
40
* still has data. If the host is tx-blocked because the ring filled up,
41
* it will request a "tx unthrottle" interrupt. If sending a serial character
42
* empties the ring below the desired threshold, we set a bit that will send
43
* up the tx unthrottle message as soon as the rx buffer becomes free.
44
*
45
* EP2in (interrupt) is used to send both rx chars and rx status messages
46
* (only "tx unthrottle" at this time) back up to the host. The first byte
47
* of the rx message indicates data (0) or status msg (1). Status messages
48
* are sent before any data.
49
*
50
* Incoming serial characters are put into the rx ring by the serial
51
* interrupt, and the EP2in buffer sent if it wasn't already in transit.
52
* When the EP2in buffer returns, the interrupt prompts us to send more
53
* rx chars (or status messages) if they are pending.
54
*
55
* Device control happens through "vendor specific" control messages on EP0.
56
* All messages are destined for the "Interface" (with the index always 0,
57
* so that if their two-port device might someday use similar firmware, we
58
* can use index=1 to refer to the second port). The messages defined are:
59
*
60
* bRequest = 0 : set baud/bits/parity
61
* 1 : unused
62
* 2 : reserved for setting HW flow control (CTSRTS)
63
* 3 : get/set "modem info" (pin states: DTR, RTS, DCD, RI, etc)
64
* 4 : set break (on/off)
65
* 5 : reserved for requesting interrupts on pin state change
66
* 6 : query buffer room or chars in tx buffer
67
* 7 : request tx unthrottle interrupt
68
*
69
* The host-side driver is set to recognize the device ID values stashed in
70
* serial EEPROM (0x06cd, 0x0103), program this firmware into place, then
71
* start it running. This firmware will use EzUSB's "renumeration" trick by
72
* simulating a bus disconnect, then reconnect with a different device ID
73
* (encoded in the desc_device descriptor below). The host driver then
74
* recognizes the new device ID and glues it to the real serial driver code.
75
*
76
* USEFUL DOCS:
77
* EzUSB Technical Reference Manual: <http://www.cypress.com/>
78
* 8051 manuals: everywhere, but try www.dalsemi.com because the EzUSB is
79
* basically the Dallas enhanced 8051 code. Remember that the EzUSB IO ports
80
* use totally different registers!
81
* USB 1.1 spec: www.usb.org
82
*
83
* HOW TO BUILD:
84
* gcc -x assembler-with-cpp -P -E -o keyspan_pda.asm keyspan_pda.s
85
* as31 -l keyspan_pda.asm
86
* mv keyspan_pda.obj keyspan_pda.hex
87
* perl ezusb_convert.pl keyspan_pda < keyspan_pda.hex > keyspan_pda_fw.h
88
* Get as31 from <http://www.pjrc.com/tech/8051/index.html>, and hack on it
89
* a bit to make it build.
90
*
91
* THANKS:
92
* Greg Kroah-Hartman, for coordinating the whole usb-serial thing.
93
* AnchorChips, for making such an incredibly useful little microcontroller.
94
* KeySpan, for making a handy, cheap ($40) widget that was so easy to take
95
* apart and trace with an ohmmeter.
96
*
97
* TODO:
98
* lots. grep for TODO. Interrupt safety needs stress-testing. Better flow
99
* control. Interrupting host upon change in DCD, etc, counting transitions.
100
* Need to find a safe device id to use (the one used by the Keyspan firmware
101
* under Windows would be ideal.. can anyone figure out what it is?). Parity.
102
* More baud rates. Oh, and the string-descriptor-length silicon bug
103
* workaround should be implemented, but I'm lazy, and the consequence is
104
* that the device name strings that show up in your kernel log will have
105
* lots of trailing binary garbage in them (appears as ????). Device strings
106
* should be made more accurate.
107
*
108
* Questions, bugs, patches to Brian.
109
*
110
* -Brian Warner <[email protected]>
111
*
112
*/
113
114
#define HIGH(x) (((x) & 0xff00) / 256)
115
#define LOW(x) ((x) & 0xff)
116
117
#define dpl1 0x84
118
#define dph1 0x85
119
#define dps 0x86
120
121
;;; our bit assignments
122
#define TX_RUNNING 0
123
#define DO_TX_UNTHROTTLE 1
124
125
;; stack from 0x60 to 0x7f: should really set SP to 0x60-1, not 0x60
126
#define STACK #0x60-1
127
128
#define EXIF 0x91
129
#define EIE 0xe8
130
.flag EUSB, EIE.0
131
.flag ES0, IE.4
132
133
#define EP0CS #0x7fb4
134
#define EP0STALLbit #0x01
135
#define IN0BUF #0x7f00
136
#define IN0BC #0x7fb5
137
#define OUT0BUF #0x7ec0
138
#define OUT0BC #0x7fc5
139
#define IN2BUF #0x7e00
140
#define IN2BC #0x7fb9
141
#define IN2CS #0x7fb8
142
#define OUT2BC #0x7fc9
143
#define OUT2CS #0x7fc8
144
#define OUT2BUF #0x7dc0
145
#define IN4BUF #0x7d00
146
#define IN4BC #0x7fbd
147
#define IN4CS #0x7fbc
148
#define OEB #0x7f9d
149
#define OUTB #0x7f97
150
#define OEC #0x7f9e
151
#define OUTC #0x7f98
152
#define PINSC #0x7f9b
153
#define PORTCCFG #0x7f95
154
#define IN07IRQ #0x7fa9
155
#define OUT07IRQ #0x7faa
156
#define IN07IEN #0x7fac
157
#define OUT07IEN #0x7fad
158
#define USBIRQ #0x7fab
159
#define USBIEN #0x7fae
160
#define USBBAV #0x7faf
161
#define USBCS #0x7fd6
162
#define SUDPTRH #0x7fd4
163
#define SUDPTRL #0x7fd5
164
#define SETUPDAT #0x7fe8
165
166
;; usb interrupt : enable is EIE.0 (0xe8), flag is EXIF.4 (0x91)
167
168
.org 0
169
ljmp start
170
;; interrupt vectors
171
.org 23H
172
ljmp serial_int
173
.byte 0
174
175
.org 43H
176
ljmp USB_Jump_Table
177
.byte 0 ; filled in by the USB core
178
179
;;; local variables. These are not initialized properly: do it by hand.
180
.org 30H
181
rx_ring_in: .byte 0
182
rx_ring_out: .byte 0
183
tx_ring_in: .byte 0
184
tx_ring_out: .byte 0
185
tx_unthrottle_threshold: .byte 0
186
187
.org 0x100H ; wants to be on a page boundary
188
USB_Jump_Table:
189
ljmp ISR_Sudav ; Setup Data Available
190
.byte 0
191
ljmp 0 ; Start of Frame
192
.byte 0
193
ljmp 0 ; Setup Data Loading
194
.byte 0
195
ljmp 0 ; Global Suspend
196
.byte 0
197
ljmp 0 ; USB Reset
198
.byte 0
199
ljmp 0 ; Reserved
200
.byte 0
201
ljmp 0 ; End Point 0 In
202
.byte 0
203
ljmp 0 ; End Point 0 Out
204
.byte 0
205
ljmp 0 ; End Point 1 In
206
.byte 0
207
ljmp 0 ; End Point 1 Out
208
.byte 0
209
ljmp ISR_Ep2in
210
.byte 0
211
ljmp ISR_Ep2out
212
.byte 0
213
214
215
.org 0x200
216
217
start: mov SP,STACK-1 ; set stack
218
;; clear local variables
219
clr a
220
mov tx_ring_in, a
221
mov tx_ring_out, a
222
mov rx_ring_in, a
223
mov rx_ring_out, a
224
mov tx_unthrottle_threshold, a
225
clr TX_RUNNING
226
clr DO_TX_UNTHROTTLE
227
228
;; clear fifo with "fe"
229
mov r1, 0
230
mov a, #0xfe
231
mov dptr, #tx_ring
232
clear_tx_ring_loop:
233
movx @dptr, a
234
inc dptr
235
djnz r1, clear_tx_ring_loop
236
237
mov a, #0xfd
238
mov dptr, #rx_ring
239
clear_rx_ring_loop:
240
movx @dptr, a
241
inc dptr
242
djnz r1, clear_rx_ring_loop
243
244
;;; turn on the RS-232 driver chip (bring the STANDBY pin low)
245
;; set OEB.1
246
mov a, #02H
247
mov dptr,OEB
248
movx @dptr,a
249
;; clear PB1
250
mov a, #00H
251
mov dptr,OUTB
252
movx @dptr,a
253
;; set OEC.[127]
254
mov a, #0x86
255
mov dptr,OEC
256
movx @dptr,a
257
;; set PORTCCFG.[01] to route TxD0,RxD0 to serial port
258
mov dptr, PORTCCFG
259
mov a, #0x03
260
movx @dptr, a
261
262
;; set up interrupts, autovectoring
263
mov dptr, USBBAV
264
movx a,@dptr
265
setb acc.0 ; AVEN bit to 0
266
movx @dptr, a
267
268
mov a,#0x01 ; enable SUDAV: setup data available (for ep0)
269
mov dptr, USBIRQ
270
movx @dptr, a ; clear SUDAVI
271
mov dptr, USBIEN
272
movx @dptr, a
273
274
mov dptr, IN07IEN
275
mov a,#0x04 ; enable IN2 int
276
movx @dptr, a
277
278
mov dptr, OUT07IEN
279
mov a,#0x04 ; enable OUT2 int
280
movx @dptr, a
281
mov dptr, OUT2BC
282
movx @dptr, a ; arm OUT2
283
284
mov a, #0x84 ; turn on RTS, DTR
285
mov dptr,OUTC
286
movx @dptr, a
287
;; setup the serial port. 9600 8N1.
288
mov a,#01010011 ; mode 1, enable rx, clear int
289
mov SCON, a
290
;; using timer2, in 16-bit baud-rate-generator mode
291
;; (xtal 12MHz, internal fosc 24MHz)
292
;; RCAP2H,RCAP2L = 65536 - fosc/(32*baud)
293
;; 57600: 0xFFF2.F, say 0xFFF3
294
;; 9600: 0xFFB1.E, say 0xFFB2
295
;; 300: 0xF63C
296
#define BAUD 9600
297
#define BAUD_TIMEOUT(rate) (65536 - (24 * 1000 * 1000) / (32 * rate))
298
#define BAUD_HIGH(rate) HIGH(BAUD_TIMEOUT(rate))
299
#define BAUD_LOW(rate) LOW(BAUD_TIMEOUT(rate))
300
301
mov T2CON, #030h ; rclk=1,tclk=1,cp=0,tr2=0(enable later)
302
mov r3, #5
303
acall set_baud
304
setb TR2
305
mov SCON, #050h
306
307
#if 0
308
mov r1, #0x40
309
mov a, #0x41
310
send:
311
mov SBUF, a
312
inc a
313
anl a, #0x3F
314
orl a, #0x40
315
; xrl a, #0x02
316
wait1:
317
jnb TI, wait1
318
clr TI
319
djnz r1, send
320
;done: sjmp done
321
322
#endif
323
324
setb EUSB
325
setb EA
326
setb ES0
327
;acall dump_stat
328
329
;; hey, what say we RENUMERATE! (TRM p.62)
330
mov a, #0
331
mov dps, a
332
mov dptr, USBCS
333
mov a, #0x02 ; DISCON=0, DISCOE=0, RENUM=1
334
movx @dptr, a
335
;; now presence pin is floating, simulating disconnect. wait 0.5s
336
mov r1, #46
337
renum_wait1:
338
mov r2, #0
339
renum_wait2:
340
mov r3, #0
341
renum_wait3:
342
djnz r3, renum_wait3
343
djnz r2, renum_wait2
344
djnz r1, renum_wait1 ; wait about n*(256^2) 6MHz clocks
345
mov a, #0x06 ; DISCON=0, DISCOE=1, RENUM=1
346
movx @dptr, a
347
;; we are back online. the host device will now re-query us
348
349
350
main: sjmp main
351
352
353
354
ISR_Sudav:
355
push dps
356
push dpl
357
push dph
358
push dpl1
359
push dph1
360
push acc
361
mov a,EXIF
362
clr acc.4
363
mov EXIF,a ; clear INT2 first
364
mov dptr, USBIRQ ; clear USB int
365
mov a,#01h
366
movx @dptr,a
367
368
;; get request type
369
mov dptr, SETUPDAT
370
movx a, @dptr
371
mov r1, a ; r1 = bmRequestType
372
inc dptr
373
movx a, @dptr
374
mov r2, a ; r2 = bRequest
375
inc dptr
376
movx a, @dptr
377
mov r3, a ; r3 = wValueL
378
inc dptr
379
movx a, @dptr
380
mov r4, a ; r4 = wValueH
381
382
;; main switch on bmRequest.type: standard or vendor
383
mov a, r1
384
anl a, #0x60
385
cjne a, #0x00, setup_bmreq_type_not_standard
386
;; standard request: now main switch is on bRequest
387
ljmp setup_bmreq_is_standard
388
389
setup_bmreq_type_not_standard:
390
;; a still has bmreq&0x60
391
cjne a, #0x40, setup_bmreq_type_not_vendor
392
;; Anchor reserves bRequest 0xa0-0xaf, we use small ones
393
;; switch on bRequest. bmRequest will always be 0x41 or 0xc1
394
cjne r2, #0x00, setup_ctrl_not_00
395
;; 00 is set baud, wValue[0] has baud rate index
396
lcall set_baud ; index in r3, carry set if error
397
jc setup_bmreq_type_not_standard__do_stall
398
ljmp setup_done_ack
399
setup_bmreq_type_not_standard__do_stall:
400
ljmp setup_stall
401
setup_ctrl_not_00:
402
cjne r2, #0x01, setup_ctrl_not_01
403
;; 01 is reserved for set bits (parity). TODO
404
ljmp setup_stall
405
setup_ctrl_not_01:
406
cjne r2, #0x02, setup_ctrl_not_02
407
;; 02 is set HW flow control. TODO
408
ljmp setup_stall
409
setup_ctrl_not_02:
410
cjne r2, #0x03, setup_ctrl_not_03
411
;; 03 is control pins (RTS, DTR).
412
ljmp control_pins ; will jump to setup_done_ack,
413
; or setup_return_one_byte
414
setup_ctrl_not_03:
415
cjne r2, #0x04, setup_ctrl_not_04
416
;; 04 is send break (really "turn break on/off"). TODO
417
cjne r3, #0x00, setup_ctrl_do_break_on
418
;; do break off: restore PORTCCFG.1 to reconnect TxD0 to serial port
419
mov dptr, PORTCCFG
420
movx a, @dptr
421
orl a, #0x02
422
movx @dptr, a
423
ljmp setup_done_ack
424
setup_ctrl_do_break_on:
425
;; do break on: clear PORTCCFG.0, set TxD high(?) (b1 low)
426
mov dptr, OUTC
427
movx a, @dptr
428
anl a, #0xfd ; ~0x02
429
movx @dptr, a
430
mov dptr, PORTCCFG
431
movx a, @dptr
432
anl a, #0xfd ; ~0x02
433
movx @dptr, a
434
ljmp setup_done_ack
435
setup_ctrl_not_04:
436
cjne r2, #0x05, setup_ctrl_not_05
437
;; 05 is set desired interrupt bitmap. TODO
438
ljmp setup_stall
439
setup_ctrl_not_05:
440
cjne r2, #0x06, setup_ctrl_not_06
441
;; 06 is query room
442
cjne r3, #0x00, setup_ctrl_06_not_00
443
;; 06, wValue[0]=0 is query write_room
444
mov a, tx_ring_out
445
setb c
446
subb a, tx_ring_in ; out-1-in = 255 - (in-out)
447
ljmp setup_return_one_byte
448
setup_ctrl_06_not_00:
449
cjne r3, #0x01, setup_ctrl_06_not_01
450
;; 06, wValue[0]=1 is query chars_in_buffer
451
mov a, tx_ring_in
452
clr c
453
subb a, tx_ring_out ; in-out
454
ljmp setup_return_one_byte
455
setup_ctrl_06_not_01:
456
ljmp setup_stall
457
setup_ctrl_not_06:
458
cjne r2, #0x07, setup_ctrl_not_07
459
;; 07 is request tx unthrottle interrupt
460
mov tx_unthrottle_threshold, r3; wValue[0] is threshold value
461
ljmp setup_done_ack
462
setup_ctrl_not_07:
463
ljmp setup_stall
464
465
setup_bmreq_type_not_vendor:
466
ljmp setup_stall
467
468
469
setup_bmreq_is_standard:
470
cjne r2, #0x00, setup_breq_not_00
471
;; 00: Get_Status (sub-switch on bmRequestType: device, ep, int)
472
cjne r1, #0x80, setup_Get_Status_not_device
473
;; Get_Status(device)
474
;; are we self-powered? no. can we do remote wakeup? no
475
;; so return two zero bytes. This is reusable
476
setup_return_two_zero_bytes:
477
mov dptr, IN0BUF
478
clr a
479
movx @dptr, a
480
inc dptr
481
movx @dptr, a
482
mov dptr, IN0BC
483
mov a, #2
484
movx @dptr, a
485
ljmp setup_done_ack
486
setup_Get_Status_not_device:
487
cjne r1, #0x82, setup_Get_Status_not_endpoint
488
;; Get_Status(endpoint)
489
;; must get stall bit for ep[wIndexL], return two bytes, bit in lsb 0
490
;; for now: cheat. TODO
491
sjmp setup_return_two_zero_bytes
492
setup_Get_Status_not_endpoint:
493
cjne r1, #0x81, setup_Get_Status_not_interface
494
;; Get_Status(interface): return two zeros
495
sjmp setup_return_two_zero_bytes
496
setup_Get_Status_not_interface:
497
ljmp setup_stall
498
499
setup_breq_not_00:
500
cjne r2, #0x01, setup_breq_not_01
501
;; 01: Clear_Feature (sub-switch on wValueL: stall, remote wakeup)
502
cjne r3, #0x00, setup_Clear_Feature_not_stall
503
;; Clear_Feature(stall). should clear a stall bit. TODO
504
ljmp setup_stall
505
setup_Clear_Feature_not_stall:
506
cjne r3, #0x01, setup_Clear_Feature_not_rwake
507
;; Clear_Feature(remote wakeup). ignored.
508
ljmp setup_done_ack
509
setup_Clear_Feature_not_rwake:
510
ljmp setup_stall
511
512
setup_breq_not_01:
513
cjne r2, #0x03, setup_breq_not_03
514
;; 03: Set_Feature (sub-switch on wValueL: stall, remote wakeup)
515
cjne r3, #0x00, setup_Set_Feature_not_stall
516
;; Set_Feature(stall). Should set a stall bit. TODO
517
ljmp setup_stall
518
setup_Set_Feature_not_stall:
519
cjne r3, #0x01, setup_Set_Feature_not_rwake
520
;; Set_Feature(remote wakeup). ignored.
521
ljmp setup_done_ack
522
setup_Set_Feature_not_rwake:
523
ljmp setup_stall
524
525
setup_breq_not_03:
526
cjne r2, #0x06, setup_breq_not_06
527
;; 06: Get_Descriptor (s-switch on wValueH: dev, config[n], string[n])
528
cjne r4, #0x01, setup_Get_Descriptor_not_device
529
;; Get_Descriptor(device)
530
mov dptr, SUDPTRH
531
mov a, #HIGH(desc_device)
532
movx @dptr, a
533
mov dptr, SUDPTRL
534
mov a, #LOW(desc_device)
535
movx @dptr, a
536
ljmp setup_done_ack
537
setup_Get_Descriptor_not_device:
538
cjne r4, #0x02, setup_Get_Descriptor_not_config
539
;; Get_Descriptor(config[n])
540
cjne r3, #0x00, setup_stall; only handle n==0
541
;; Get_Descriptor(config[0])
542
mov dptr, SUDPTRH
543
mov a, #HIGH(desc_config1)
544
movx @dptr, a
545
mov dptr, SUDPTRL
546
mov a, #LOW(desc_config1)
547
movx @dptr, a
548
ljmp setup_done_ack
549
setup_Get_Descriptor_not_config:
550
cjne r4, #0x03, setup_Get_Descriptor_not_string
551
;; Get_Descriptor(string[wValueL])
552
;; if (wValueL >= maxstrings) stall
553
mov a, #((desc_strings_end-desc_strings)/2)
554
clr c
555
subb a,r3 ; a=4, r3 = 0..3 . if a<=0 then stall
556
jc setup_stall
557
jz setup_stall
558
mov a, r3
559
add a, r3 ; a = 2*wValueL
560
mov dptr, #desc_strings
561
add a, dpl
562
mov dpl, a
563
mov a, #0
564
addc a, dph
565
mov dph, a ; dph = desc_strings[a]. big endian! (handy)
566
;; it looks like my adapter uses a revision of the EZUSB that
567
;; contains "rev D errata number 8", as hinted in the EzUSB example
568
;; code. I cannot find an actual errata description on the Cypress
569
;; web site, but from the example code it looks like this bug causes
570
;; the length of string descriptors to be read incorrectly, possibly
571
;; sending back more characters than the descriptor has. The workaround
572
;; is to manually send out all of the data. The consequence of not
573
;; using the workaround is that the strings gathered by the kernel
574
;; driver are too long and are filled with trailing garbage (including
575
;; leftover strings). Writing this out by hand is a nuisance, so for
576
;; now I will just live with the bug.
577
movx a, @dptr
578
mov r1, a
579
inc dptr
580
movx a, @dptr
581
mov r2, a
582
mov dptr, SUDPTRH
583
mov a, r1
584
movx @dptr, a
585
mov dptr, SUDPTRL
586
mov a, r2
587
movx @dptr, a
588
;; done
589
ljmp setup_done_ack
590
591
setup_Get_Descriptor_not_string:
592
ljmp setup_stall
593
594
setup_breq_not_06:
595
cjne r2, #0x08, setup_breq_not_08
596
;; Get_Configuration. always 1. return one byte.
597
;; this is reusable
598
mov a, #1
599
setup_return_one_byte:
600
mov dptr, IN0BUF
601
movx @dptr, a
602
mov a, #1
603
mov dptr, IN0BC
604
movx @dptr, a
605
ljmp setup_done_ack
606
setup_breq_not_08:
607
cjne r2, #0x09, setup_breq_not_09
608
;; 09: Set_Configuration. ignored.
609
ljmp setup_done_ack
610
setup_breq_not_09:
611
cjne r2, #0x0a, setup_breq_not_0a
612
;; 0a: Get_Interface. get the current altsetting for int[wIndexL]
613
;; since we only have one interface, ignore wIndexL, return a 0
614
mov a, #0
615
ljmp setup_return_one_byte
616
setup_breq_not_0a:
617
cjne r2, #0x0b, setup_breq_not_0b
618
;; 0b: Set_Interface. set altsetting for interface[wIndexL]. ignored
619
ljmp setup_done_ack
620
setup_breq_not_0b:
621
ljmp setup_stall
622
623
624
setup_done_ack:
625
;; now clear HSNAK
626
mov dptr, EP0CS
627
mov a, #0x02
628
movx @dptr, a
629
sjmp setup_done
630
setup_stall:
631
;; unhandled. STALL
632
;EP0CS |= bmEPSTALL
633
mov dptr, EP0CS
634
movx a, @dptr
635
orl a, EP0STALLbit
636
movx @dptr, a
637
sjmp setup_done
638
639
setup_done:
640
pop acc
641
pop dph1
642
pop dpl1
643
pop dph
644
pop dpl
645
pop dps
646
reti
647
648
;;; ==============================================================
649
650
set_baud: ; baud index in r3
651
;; verify a < 10
652
mov a, r3
653
jb ACC.7, set_baud__badbaud
654
clr c
655
subb a, #10
656
jnc set_baud__badbaud
657
mov a, r3
658
rl a ; a = index*2
659
add a, #LOW(baud_table)
660
mov dpl, a
661
mov a, #HIGH(baud_table)
662
addc a, #0
663
mov dph, a
664
;; TODO: shut down xmit/receive
665
;; TODO: wait for current xmit char to leave
666
;; TODO: shut down timer to avoid partial-char glitch
667
movx a,@dptr ; BAUD_HIGH
668
mov RCAP2H, a
669
mov TH2, a
670
inc dptr
671
movx a,@dptr ; BAUD_LOW
672
mov RCAP2L, a
673
mov TL2, a
674
;; TODO: restart xmit/receive
675
;; TODO: reenable interrupts, resume tx if pending
676
clr c ; c=0: success
677
ret
678
set_baud__badbaud:
679
setb c ; c=1: failure
680
ret
681
682
;;; ==================================================
683
control_pins:
684
cjne r1, #0x41, control_pins_in
685
control_pins_out:
686
mov a, r3 ; wValue[0] holds new bits: b7 is new DTR, b2 is new RTS
687
xrl a, #0xff ; 1 means active, 0V, +12V ?
688
anl a, #0x84
689
mov r3, a
690
mov dptr, OUTC
691
movx a, @dptr ; only change bits 7 and 2
692
anl a, #0x7b ; ~0x84
693
orl a, r3
694
movx @dptr, a ; other pins are inputs, bits ignored
695
ljmp setup_done_ack
696
control_pins_in:
697
mov dptr, PINSC
698
movx a, @dptr
699
xrl a, #0xff
700
ljmp setup_return_one_byte
701
702
;;; ========================================
703
704
ISR_Ep2in:
705
push dps
706
push dpl
707
push dph
708
push dpl1
709
push dph1
710
push acc
711
mov a,EXIF
712
clr acc.4
713
mov EXIF,a ; clear INT2 first
714
mov dptr, IN07IRQ ; clear USB int
715
mov a,#04h
716
movx @dptr,a
717
718
;; do stuff
719
lcall start_in
720
721
pop acc
722
pop dph1
723
pop dpl1
724
pop dph
725
pop dpl
726
pop dps
727
reti
728
729
ISR_Ep2out:
730
push dps
731
push dpl
732
push dph
733
push dpl1
734
push dph1
735
push acc
736
mov a,EXIF
737
clr acc.4
738
mov EXIF,a ; clear INT2 first
739
mov dptr, OUT07IRQ ; clear USB int
740
mov a,#04h
741
movx @dptr,a
742
743
;; do stuff
744
745
;; copy data into buffer. for now, assume we will have enough space
746
mov dptr, OUT2BC ; get byte count
747
movx a,@dptr
748
mov r1, a
749
clr a
750
mov dps, a
751
mov dptr, OUT2BUF ; load DPTR0 with source
752
mov dph1, #HIGH(tx_ring) ; load DPTR1 with target
753
mov dpl1, tx_ring_in
754
OUT_loop:
755
movx a,@dptr ; read
756
inc dps ; switch to DPTR1: target
757
inc dpl1 ; target = tx_ring_in+1
758
movx @dptr,a ; store
759
mov a,dpl1
760
cjne a, tx_ring_out, OUT_no_overflow
761
sjmp OUT_overflow
762
OUT_no_overflow:
763
inc tx_ring_in ; tx_ring_in++
764
inc dps ; switch to DPTR0: source
765
inc dptr
766
djnz r1, OUT_loop
767
sjmp OUT_done
768
OUT_overflow:
769
;; signal overflow
770
;; fall through
771
OUT_done:
772
;; ack
773
mov dptr,OUT2BC
774
movx @dptr,a
775
776
;; start tx
777
acall maybe_start_tx
778
;acall dump_stat
779
780
pop acc
781
pop dph1
782
pop dpl1
783
pop dph
784
pop dpl
785
pop dps
786
reti
787
788
dump_stat:
789
;; fill in EP4in with a debugging message:
790
;; tx_ring_in, tx_ring_out, rx_ring_in, rx_ring_out
791
;; tx_active
792
;; tx_ring[0..15]
793
;; 0xfc
794
;; rx_ring[0..15]
795
clr a
796
mov dps, a
797
798
mov dptr, IN4CS
799
movx a, @dptr
800
jb acc.1, dump_stat__done; busy: cannot dump, old one still pending
801
mov dptr, IN4BUF
802
803
mov a, tx_ring_in
804
movx @dptr, a
805
inc dptr
806
mov a, tx_ring_out
807
movx @dptr, a
808
inc dptr
809
810
mov a, rx_ring_in
811
movx @dptr, a
812
inc dptr
813
mov a, rx_ring_out
814
movx @dptr, a
815
inc dptr
816
817
clr a
818
jnb TX_RUNNING, dump_stat__no_tx_running
819
inc a
820
dump_stat__no_tx_running:
821
movx @dptr, a
822
inc dptr
823
;; tx_ring[0..15]
824
inc dps
825
mov dptr, #tx_ring ; DPTR1: source
826
mov r1, #16
827
dump_stat__tx_ring_loop:
828
movx a, @dptr
829
inc dptr
830
inc dps
831
movx @dptr, a
832
inc dptr
833
inc dps
834
djnz r1, dump_stat__tx_ring_loop
835
inc dps
836
837
mov a, #0xfc
838
movx @dptr, a
839
inc dptr
840
841
;; rx_ring[0..15]
842
inc dps
843
mov dptr, #rx_ring ; DPTR1: source
844
mov r1, #16
845
dump_stat__rx_ring_loop:
846
movx a, @dptr
847
inc dptr
848
inc dps
849
movx @dptr, a
850
inc dptr
851
inc dps
852
djnz r1, dump_stat__rx_ring_loop
853
854
;; now send it
855
clr a
856
mov dps, a
857
mov dptr, IN4BC
858
mov a, #38
859
movx @dptr, a
860
dump_stat__done:
861
ret
862
863
;;; ============================================================
864
865
maybe_start_tx:
866
;; make sure the tx process is running.
867
jb TX_RUNNING, start_tx_done
868
start_tx:
869
;; is there work to be done?
870
mov a, tx_ring_in
871
cjne a,tx_ring_out, start_tx__work
872
ret ; no work
873
start_tx__work:
874
;; tx was not running. send the first character, setup the TI int
875
inc tx_ring_out ; [++tx_ring_out]
876
mov dph, #HIGH(tx_ring)
877
mov dpl, tx_ring_out
878
movx a, @dptr
879
mov sbuf, a
880
setb TX_RUNNING
881
start_tx_done:
882
;; can we unthrottle the host tx process?
883
;; step 1: do we care?
884
mov a, #0
885
cjne a, tx_unthrottle_threshold, start_tx__maybe_unthrottle_tx
886
;; nope
887
start_tx_really_done:
888
ret
889
start_tx__maybe_unthrottle_tx:
890
;; step 2: is there now room?
891
mov a, tx_ring_out
892
setb c
893
subb a, tx_ring_in
894
;; a is now write_room. If thresh >= a, we can unthrottle
895
clr c
896
subb a, tx_unthrottle_threshold
897
jc start_tx_really_done ; nope
898
;; yes, we can unthrottle. remove the threshold and mark a request
899
mov tx_unthrottle_threshold, #0
900
setb DO_TX_UNTHROTTLE
901
;; prod rx, which will actually send the message when in2 becomes free
902
ljmp start_in
903
904
905
serial_int:
906
push dps
907
push dpl
908
push dph
909
push dpl1
910
push dph1
911
push acc
912
jnb TI, serial_int__not_tx
913
;; tx finished. send another character if we have one
914
clr TI ; clear int
915
clr TX_RUNNING
916
lcall start_tx
917
serial_int__not_tx:
918
jnb RI, serial_int__not_rx
919
lcall get_rx_char
920
clr RI ; clear int
921
serial_int__not_rx:
922
;; return
923
pop acc
924
pop dph1
925
pop dpl1
926
pop dph
927
pop dpl
928
pop dps
929
reti
930
931
get_rx_char:
932
mov dph, #HIGH(rx_ring)
933
mov dpl, rx_ring_in
934
inc dpl ; target = rx_ring_in+1
935
mov a, sbuf
936
movx @dptr, a
937
;; check for overflow before incrementing rx_ring_in
938
mov a, dpl
939
cjne a, rx_ring_out, get_rx_char__no_overflow
940
;; signal overflow
941
ret
942
get_rx_char__no_overflow:
943
inc rx_ring_in
944
;; kick off USB INpipe
945
acall start_in
946
ret
947
948
start_in:
949
;; check if the inpipe is already running.
950
mov dptr, IN2CS
951
movx a, @dptr
952
jb acc.1, start_in__done; int will handle it
953
jb DO_TX_UNTHROTTLE, start_in__do_tx_unthrottle
954
;; see if there is any work to do. a serial interrupt might occur
955
;; during this sequence?
956
mov a, rx_ring_in
957
cjne a, rx_ring_out, start_in__have_work
958
ret ; nope
959
start_in__have_work:
960
;; now copy as much data as possible into the pipe. 63 bytes max.
961
clr a
962
mov dps, a
963
mov dph, #HIGH(rx_ring) ; load DPTR0 with source
964
inc dps
965
mov dptr, IN2BUF ; load DPTR1 with target
966
movx @dptr, a ; in[0] signals that rest of IN is rx data
967
inc dptr
968
inc dps
969
;; loop until we run out of data, or we have copied 64 bytes
970
mov r1, #1 ; INbuf size counter
971
start_in__loop:
972
mov a, rx_ring_in
973
cjne a, rx_ring_out, start_inlocal_irq_enablell_copying
974
sjmp start_in__kick
975
start_inlocal_irq_enablell_copying:
976
inc rx_ring_out
977
mov dpl, rx_ring_out
978
movx a, @dptr
979
inc dps
980
movx @dptr, a ; write into IN buffer
981
inc dptr
982
inc dps
983
inc r1
984
cjne r1, #64, start_in__loop; loop
985
start_in__kick:
986
;; either we ran out of data, or we copied 64 bytes. r1 has byte count
987
;; kick off IN
988
mov dptr, IN2BC
989
mov a, r1
990
jz start_in__done
991
movx @dptr, a
992
;; done
993
start_in__done:
994
;acall dump_stat
995
ret
996
start_in__do_tx_unthrottle:
997
;; special sequence: send a tx unthrottle message
998
clr DO_TX_UNTHROTTLE
999
clr a
1000
mov dps, a
1001
mov dptr, IN2BUF
1002
mov a, #1
1003
movx @dptr, a
1004
inc dptr
1005
mov a, #2
1006
movx @dptr, a
1007
mov dptr, IN2BC
1008
movx @dptr, a
1009
ret
1010
1011
putchar:
1012
clr TI
1013
mov SBUF, a
1014
putchar_wait:
1015
jnb TI, putchar_wait
1016
clr TI
1017
ret
1018
1019
1020
baud_table: ; baud_high, then baud_low
1021
;; baud[0]: 110
1022
.byte BAUD_HIGH(110)
1023
.byte BAUD_LOW(110)
1024
;; baud[1]: 300
1025
.byte BAUD_HIGH(300)
1026
.byte BAUD_LOW(300)
1027
;; baud[2]: 1200
1028
.byte BAUD_HIGH(1200)
1029
.byte BAUD_LOW(1200)
1030
;; baud[3]: 2400
1031
.byte BAUD_HIGH(2400)
1032
.byte BAUD_LOW(2400)
1033
;; baud[4]: 4800
1034
.byte BAUD_HIGH(4800)
1035
.byte BAUD_LOW(4800)
1036
;; baud[5]: 9600
1037
.byte BAUD_HIGH(9600)
1038
.byte BAUD_LOW(9600)
1039
;; baud[6]: 19200
1040
.byte BAUD_HIGH(19200)
1041
.byte BAUD_LOW(19200)
1042
;; baud[7]: 38400
1043
.byte BAUD_HIGH(38400)
1044
.byte BAUD_LOW(38400)
1045
;; baud[8]: 57600
1046
.byte BAUD_HIGH(57600)
1047
.byte BAUD_LOW(57600)
1048
;; baud[9]: 115200
1049
.byte BAUD_HIGH(115200)
1050
.byte BAUD_LOW(115200)
1051
1052
desc_device:
1053
.byte 0x12, 0x01, 0x00, 0x01, 0xff, 0xff, 0xff, 0x40
1054
.byte 0xcd, 0x06, 0x04, 0x01, 0x89, 0xab, 1, 2, 3, 0x01
1055
;;; The "real" device id, which must match the host driver, is that
1056
;;; "0xcd 0x06 0x04 0x01" sequence, which is 0x06cd, 0x0104
1057
1058
desc_config1:
1059
.byte 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32
1060
.byte 0x09, 0x04, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0x00
1061
.byte 0x07, 0x05, 0x82, 0x03, 0x40, 0x00, 0x01
1062
.byte 0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00
1063
1064
desc_strings:
1065
.word string_langids, string_mfg, string_product, string_serial
1066
desc_strings_end:
1067
1068
string_langids: .byte string_langids_end-string_langids
1069
.byte 3
1070
.word 0
1071
string_langids_end:
1072
1073
;; sigh. These strings are Unicode, meaning UTF16? 2 bytes each. Now
1074
;; *that* is a pain in the ass to encode. And they are little-endian
1075
;; too. Use this perl snippet to get the bytecodes:
1076
/* while (<>) {
1077
@c = split(//);
1078
foreach $c (@c) {
1079
printf("0x%02x, 0x00, ", ord($c));
1080
}
1081
}
1082
*/
1083
1084
string_mfg: .byte string_mfg_end-string_mfg
1085
.byte 3
1086
; .byte "ACME usb widgets"
1087
.byte 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x75, 0x00, 0x73, 0x00, 0x62, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00, 0x73, 0x00
1088
string_mfg_end:
1089
1090
string_product: .byte string_product_end-string_product
1091
.byte 3
1092
; .byte "ACME USB serial widget"
1093
.byte 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x55, 0x00, 0x53, 0x00, 0x42, 0x00, 0x20, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00
1094
string_product_end:
1095
1096
string_serial: .byte string_serial_end-string_serial
1097
.byte 3
1098
; .byte "47"
1099
.byte 0x34, 0x00, 0x37, 0x00
1100
string_serial_end:
1101
1102
;;; ring buffer memory
1103
;; tx_ring_in+1 is where the next input byte will go
1104
;; [tx_ring_out] has been sent
1105
;; if tx_ring_in == tx_ring_out, theres no work to do
1106
;; there are (tx_ring_in - tx_ring_out) chars to be written
1107
;; dont let _in lap _out
1108
;; cannot inc if tx_ring_in+1 == tx_ring_out
1109
;; write [tx_ring_in+1] then tx_ring_in++
1110
;; if (tx_ring_in+1 == tx_ring_out), overflow
1111
;; else tx_ring_in++
1112
;; read/send [tx_ring_out+1], then tx_ring_out++
1113
1114
;; rx_ring_in works the same way
1115
1116
.org 0x1000
1117
tx_ring:
1118
.skip 0x100 ; 256 bytes
1119
rx_ring:
1120
.skip 0x100 ; 256 bytes
1121
1122
1123
.END
1124
1125
1126