PageRenderTime 53ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/firmware/keyspan_pda/keyspan_pda.S

https://github.com/mstsirkin/linux
Assembly | 1124 lines | 1124 code | 0 blank | 0 comment | 7 complexity | 43c076c59b38c3898d8c3c5f172740d0 MD5 | raw file
  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 <warner@lothar.com>
  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 <warner@lothar.com>
  111. *
  112. */
  113. #define HIGH(x) (((x) & 0xff00) / 256)
  114. #define LOW(x) ((x) & 0xff)
  115. #define dpl1 0x84
  116. #define dph1 0x85
  117. #define dps 0x86
  118. ;;; our bit assignments
  119. #define TX_RUNNING 0
  120. #define DO_TX_UNTHROTTLE 1
  121. ;; stack from 0x60 to 0x7f: should really set SP to 0x60-1, not 0x60
  122. #define STACK #0x60-1
  123. #define EXIF 0x91
  124. #define EIE 0xe8
  125. .flag EUSB, EIE.0
  126. .flag ES0, IE.4
  127. #define EP0CS #0x7fb4
  128. #define EP0STALLbit #0x01
  129. #define IN0BUF #0x7f00
  130. #define IN0BC #0x7fb5
  131. #define OUT0BUF #0x7ec0
  132. #define OUT0BC #0x7fc5
  133. #define IN2BUF #0x7e00
  134. #define IN2BC #0x7fb9
  135. #define IN2CS #0x7fb8
  136. #define OUT2BC #0x7fc9
  137. #define OUT2CS #0x7fc8
  138. #define OUT2BUF #0x7dc0
  139. #define IN4BUF #0x7d00
  140. #define IN4BC #0x7fbd
  141. #define IN4CS #0x7fbc
  142. #define OEB #0x7f9d
  143. #define OUTB #0x7f97
  144. #define OEC #0x7f9e
  145. #define OUTC #0x7f98
  146. #define PINSC #0x7f9b
  147. #define PORTCCFG #0x7f95
  148. #define IN07IRQ #0x7fa9
  149. #define OUT07IRQ #0x7faa
  150. #define IN07IEN #0x7fac
  151. #define OUT07IEN #0x7fad
  152. #define USBIRQ #0x7fab
  153. #define USBIEN #0x7fae
  154. #define USBBAV #0x7faf
  155. #define USBCS #0x7fd6
  156. #define SUDPTRH #0x7fd4
  157. #define SUDPTRL #0x7fd5
  158. #define SETUPDAT #0x7fe8
  159. ;; usb interrupt : enable is EIE.0 (0xe8), flag is EXIF.4 (0x91)
  160. .org 0
  161. ljmp start
  162. ;; interrupt vectors
  163. .org 23H
  164. ljmp serial_int
  165. .byte 0
  166. .org 43H
  167. ljmp USB_Jump_Table
  168. .byte 0 ; filled in by the USB core
  169. ;;; local variables. These are not initialized properly: do it by hand.
  170. .org 30H
  171. rx_ring_in: .byte 0
  172. rx_ring_out: .byte 0
  173. tx_ring_in: .byte 0
  174. tx_ring_out: .byte 0
  175. tx_unthrottle_threshold: .byte 0
  176. .org 0x100H ; wants to be on a page boundary
  177. USB_Jump_Table:
  178. ljmp ISR_Sudav ; Setup Data Available
  179. .byte 0
  180. ljmp 0 ; Start of Frame
  181. .byte 0
  182. ljmp 0 ; Setup Data Loading
  183. .byte 0
  184. ljmp 0 ; Global Suspend
  185. .byte 0
  186. ljmp 0 ; USB Reset
  187. .byte 0
  188. ljmp 0 ; Reserved
  189. .byte 0
  190. ljmp 0 ; End Point 0 In
  191. .byte 0
  192. ljmp 0 ; End Point 0 Out
  193. .byte 0
  194. ljmp 0 ; End Point 1 In
  195. .byte 0
  196. ljmp 0 ; End Point 1 Out
  197. .byte 0
  198. ljmp ISR_Ep2in
  199. .byte 0
  200. ljmp ISR_Ep2out
  201. .byte 0
  202. .org 0x200
  203. start: mov SP,STACK-1 ; set stack
  204. ;; clear local variables
  205. clr a
  206. mov tx_ring_in, a
  207. mov tx_ring_out, a
  208. mov rx_ring_in, a
  209. mov rx_ring_out, a
  210. mov tx_unthrottle_threshold, a
  211. clr TX_RUNNING
  212. clr DO_TX_UNTHROTTLE
  213. ;; clear fifo with "fe"
  214. mov r1, 0
  215. mov a, #0xfe
  216. mov dptr, #tx_ring
  217. clear_tx_ring_loop:
  218. movx @dptr, a
  219. inc dptr
  220. djnz r1, clear_tx_ring_loop
  221. mov a, #0xfd
  222. mov dptr, #rx_ring
  223. clear_rx_ring_loop:
  224. movx @dptr, a
  225. inc dptr
  226. djnz r1, clear_rx_ring_loop
  227. ;;; turn on the RS-232 driver chip (bring the STANDBY pin low)
  228. ;; set OEB.1
  229. mov a, #02H
  230. mov dptr,OEB
  231. movx @dptr,a
  232. ;; clear PB1
  233. mov a, #00H
  234. mov dptr,OUTB
  235. movx @dptr,a
  236. ;; set OEC.[127]
  237. mov a, #0x86
  238. mov dptr,OEC
  239. movx @dptr,a
  240. ;; set PORTCCFG.[01] to route TxD0,RxD0 to serial port
  241. mov dptr, PORTCCFG
  242. mov a, #0x03
  243. movx @dptr, a
  244. ;; set up interrupts, autovectoring
  245. mov dptr, USBBAV
  246. movx a,@dptr
  247. setb acc.0 ; AVEN bit to 0
  248. movx @dptr, a
  249. mov a,#0x01 ; enable SUDAV: setup data available (for ep0)
  250. mov dptr, USBIRQ
  251. movx @dptr, a ; clear SUDAVI
  252. mov dptr, USBIEN
  253. movx @dptr, a
  254. mov dptr, IN07IEN
  255. mov a,#0x04 ; enable IN2 int
  256. movx @dptr, a
  257. mov dptr, OUT07IEN
  258. mov a,#0x04 ; enable OUT2 int
  259. movx @dptr, a
  260. mov dptr, OUT2BC
  261. movx @dptr, a ; arm OUT2
  262. mov a, #0x84 ; turn on RTS, DTR
  263. mov dptr,OUTC
  264. movx @dptr, a
  265. ;; setup the serial port. 9600 8N1.
  266. mov a,#01010011 ; mode 1, enable rx, clear int
  267. mov SCON, a
  268. ;; using timer2, in 16-bit baud-rate-generator mode
  269. ;; (xtal 12MHz, internal fosc 24MHz)
  270. ;; RCAP2H,RCAP2L = 65536 - fosc/(32*baud)
  271. ;; 57600: 0xFFF2.F, say 0xFFF3
  272. ;; 9600: 0xFFB1.E, say 0xFFB2
  273. ;; 300: 0xF63C
  274. #define BAUD 9600
  275. #define BAUD_TIMEOUT(rate) (65536 - (24 * 1000 * 1000) / (32 * rate))
  276. #define BAUD_HIGH(rate) HIGH(BAUD_TIMEOUT(rate))
  277. #define BAUD_LOW(rate) LOW(BAUD_TIMEOUT(rate))
  278. mov T2CON, #030h ; rclk=1,tclk=1,cp=0,tr2=0(enable later)
  279. mov r3, #5
  280. acall set_baud
  281. setb TR2
  282. mov SCON, #050h
  283. #if 0
  284. mov r1, #0x40
  285. mov a, #0x41
  286. send:
  287. mov SBUF, a
  288. inc a
  289. anl a, #0x3F
  290. orl a, #0x40
  291. ; xrl a, #0x02
  292. wait1:
  293. jnb TI, wait1
  294. clr TI
  295. djnz r1, send
  296. ;done: sjmp done
  297. #endif
  298. setb EUSB
  299. setb EA
  300. setb ES0
  301. ;acall dump_stat
  302. ;; hey, what say we RENUMERATE! (TRM p.62)
  303. mov a, #0
  304. mov dps, a
  305. mov dptr, USBCS
  306. mov a, #0x02 ; DISCON=0, DISCOE=0, RENUM=1
  307. movx @dptr, a
  308. ;; now presence pin is floating, simulating disconnect. wait 0.5s
  309. mov r1, #46
  310. renum_wait1:
  311. mov r2, #0
  312. renum_wait2:
  313. mov r3, #0
  314. renum_wait3:
  315. djnz r3, renum_wait3
  316. djnz r2, renum_wait2
  317. djnz r1, renum_wait1 ; wait about n*(256^2) 6MHz clocks
  318. mov a, #0x06 ; DISCON=0, DISCOE=1, RENUM=1
  319. movx @dptr, a
  320. ;; we are back online. the host device will now re-query us
  321. main: sjmp main
  322. ISR_Sudav:
  323. push dps
  324. push dpl
  325. push dph
  326. push dpl1
  327. push dph1
  328. push acc
  329. mov a,EXIF
  330. clr acc.4
  331. mov EXIF,a ; clear INT2 first
  332. mov dptr, USBIRQ ; clear USB int
  333. mov a,#01h
  334. movx @dptr,a
  335. ;; get request type
  336. mov dptr, SETUPDAT
  337. movx a, @dptr
  338. mov r1, a ; r1 = bmRequestType
  339. inc dptr
  340. movx a, @dptr
  341. mov r2, a ; r2 = bRequest
  342. inc dptr
  343. movx a, @dptr
  344. mov r3, a ; r3 = wValueL
  345. inc dptr
  346. movx a, @dptr
  347. mov r4, a ; r4 = wValueH
  348. ;; main switch on bmRequest.type: standard or vendor
  349. mov a, r1
  350. anl a, #0x60
  351. cjne a, #0x00, setup_bmreq_type_not_standard
  352. ;; standard request: now main switch is on bRequest
  353. ljmp setup_bmreq_is_standard
  354. setup_bmreq_type_not_standard:
  355. ;; a still has bmreq&0x60
  356. cjne a, #0x40, setup_bmreq_type_not_vendor
  357. ;; Anchor reserves bRequest 0xa0-0xaf, we use small ones
  358. ;; switch on bRequest. bmRequest will always be 0x41 or 0xc1
  359. cjne r2, #0x00, setup_ctrl_not_00
  360. ;; 00 is set baud, wValue[0] has baud rate index
  361. lcall set_baud ; index in r3, carry set if error
  362. jc setup_bmreq_type_not_standard__do_stall
  363. ljmp setup_done_ack
  364. setup_bmreq_type_not_standard__do_stall:
  365. ljmp setup_stall
  366. setup_ctrl_not_00:
  367. cjne r2, #0x01, setup_ctrl_not_01
  368. ;; 01 is reserved for set bits (parity). TODO
  369. ljmp setup_stall
  370. setup_ctrl_not_01:
  371. cjne r2, #0x02, setup_ctrl_not_02
  372. ;; 02 is set HW flow control. TODO
  373. ljmp setup_stall
  374. setup_ctrl_not_02:
  375. cjne r2, #0x03, setup_ctrl_not_03
  376. ;; 03 is control pins (RTS, DTR).
  377. ljmp control_pins ; will jump to setup_done_ack,
  378. ; or setup_return_one_byte
  379. setup_ctrl_not_03:
  380. cjne r2, #0x04, setup_ctrl_not_04
  381. ;; 04 is send break (really "turn break on/off"). TODO
  382. cjne r3, #0x00, setup_ctrl_do_break_on
  383. ;; do break off: restore PORTCCFG.1 to reconnect TxD0 to serial port
  384. mov dptr, PORTCCFG
  385. movx a, @dptr
  386. orl a, #0x02
  387. movx @dptr, a
  388. ljmp setup_done_ack
  389. setup_ctrl_do_break_on:
  390. ;; do break on: clear PORTCCFG.0, set TxD high(?) (b1 low)
  391. mov dptr, OUTC
  392. movx a, @dptr
  393. anl a, #0xfd ; ~0x02
  394. movx @dptr, a
  395. mov dptr, PORTCCFG
  396. movx a, @dptr
  397. anl a, #0xfd ; ~0x02
  398. movx @dptr, a
  399. ljmp setup_done_ack
  400. setup_ctrl_not_04:
  401. cjne r2, #0x05, setup_ctrl_not_05
  402. ;; 05 is set desired interrupt bitmap. TODO
  403. ljmp setup_stall
  404. setup_ctrl_not_05:
  405. cjne r2, #0x06, setup_ctrl_not_06
  406. ;; 06 is query room
  407. cjne r3, #0x00, setup_ctrl_06_not_00
  408. ;; 06, wValue[0]=0 is query write_room
  409. mov a, tx_ring_out
  410. setb c
  411. subb a, tx_ring_in ; out-1-in = 255 - (in-out)
  412. ljmp setup_return_one_byte
  413. setup_ctrl_06_not_00:
  414. cjne r3, #0x01, setup_ctrl_06_not_01
  415. ;; 06, wValue[0]=1 is query chars_in_buffer
  416. mov a, tx_ring_in
  417. clr c
  418. subb a, tx_ring_out ; in-out
  419. ljmp setup_return_one_byte
  420. setup_ctrl_06_not_01:
  421. ljmp setup_stall
  422. setup_ctrl_not_06:
  423. cjne r2, #0x07, setup_ctrl_not_07
  424. ;; 07 is request tx unthrottle interrupt
  425. mov tx_unthrottle_threshold, r3; wValue[0] is threshold value
  426. ljmp setup_done_ack
  427. setup_ctrl_not_07:
  428. ljmp setup_stall
  429. setup_bmreq_type_not_vendor:
  430. ljmp setup_stall
  431. setup_bmreq_is_standard:
  432. cjne r2, #0x00, setup_breq_not_00
  433. ;; 00: Get_Status (sub-switch on bmRequestType: device, ep, int)
  434. cjne r1, #0x80, setup_Get_Status_not_device
  435. ;; Get_Status(device)
  436. ;; are we self-powered? no. can we do remote wakeup? no
  437. ;; so return two zero bytes. This is reusable
  438. setup_return_two_zero_bytes:
  439. mov dptr, IN0BUF
  440. clr a
  441. movx @dptr, a
  442. inc dptr
  443. movx @dptr, a
  444. mov dptr, IN0BC
  445. mov a, #2
  446. movx @dptr, a
  447. ljmp setup_done_ack
  448. setup_Get_Status_not_device:
  449. cjne r1, #0x82, setup_Get_Status_not_endpoint
  450. ;; Get_Status(endpoint)
  451. ;; must get stall bit for ep[wIndexL], return two bytes, bit in lsb 0
  452. ;; for now: cheat. TODO
  453. sjmp setup_return_two_zero_bytes
  454. setup_Get_Status_not_endpoint:
  455. cjne r1, #0x81, setup_Get_Status_not_interface
  456. ;; Get_Status(interface): return two zeros
  457. sjmp setup_return_two_zero_bytes
  458. setup_Get_Status_not_interface:
  459. ljmp setup_stall
  460. setup_breq_not_00:
  461. cjne r2, #0x01, setup_breq_not_01
  462. ;; 01: Clear_Feature (sub-switch on wValueL: stall, remote wakeup)
  463. cjne r3, #0x00, setup_Clear_Feature_not_stall
  464. ;; Clear_Feature(stall). should clear a stall bit. TODO
  465. ljmp setup_stall
  466. setup_Clear_Feature_not_stall:
  467. cjne r3, #0x01, setup_Clear_Feature_not_rwake
  468. ;; Clear_Feature(remote wakeup). ignored.
  469. ljmp setup_done_ack
  470. setup_Clear_Feature_not_rwake:
  471. ljmp setup_stall
  472. setup_breq_not_01:
  473. cjne r2, #0x03, setup_breq_not_03
  474. ;; 03: Set_Feature (sub-switch on wValueL: stall, remote wakeup)
  475. cjne r3, #0x00, setup_Set_Feature_not_stall
  476. ;; Set_Feature(stall). Should set a stall bit. TODO
  477. ljmp setup_stall
  478. setup_Set_Feature_not_stall:
  479. cjne r3, #0x01, setup_Set_Feature_not_rwake
  480. ;; Set_Feature(remote wakeup). ignored.
  481. ljmp setup_done_ack
  482. setup_Set_Feature_not_rwake:
  483. ljmp setup_stall
  484. setup_breq_not_03:
  485. cjne r2, #0x06, setup_breq_not_06
  486. ;; 06: Get_Descriptor (s-switch on wValueH: dev, config[n], string[n])
  487. cjne r4, #0x01, setup_Get_Descriptor_not_device
  488. ;; Get_Descriptor(device)
  489. mov dptr, SUDPTRH
  490. mov a, #HIGH(desc_device)
  491. movx @dptr, a
  492. mov dptr, SUDPTRL
  493. mov a, #LOW(desc_device)
  494. movx @dptr, a
  495. ljmp setup_done_ack
  496. setup_Get_Descriptor_not_device:
  497. cjne r4, #0x02, setup_Get_Descriptor_not_config
  498. ;; Get_Descriptor(config[n])
  499. cjne r3, #0x00, setup_stall; only handle n==0
  500. ;; Get_Descriptor(config[0])
  501. mov dptr, SUDPTRH
  502. mov a, #HIGH(desc_config1)
  503. movx @dptr, a
  504. mov dptr, SUDPTRL
  505. mov a, #LOW(desc_config1)
  506. movx @dptr, a
  507. ljmp setup_done_ack
  508. setup_Get_Descriptor_not_config:
  509. cjne r4, #0x03, setup_Get_Descriptor_not_string
  510. ;; Get_Descriptor(string[wValueL])
  511. ;; if (wValueL >= maxstrings) stall
  512. mov a, #((desc_strings_end-desc_strings)/2)
  513. clr c
  514. subb a,r3 ; a=4, r3 = 0..3 . if a<=0 then stall
  515. jc setup_stall
  516. jz setup_stall
  517. mov a, r3
  518. add a, r3 ; a = 2*wValueL
  519. mov dptr, #desc_strings
  520. add a, dpl
  521. mov dpl, a
  522. mov a, #0
  523. addc a, dph
  524. mov dph, a ; dph = desc_strings[a]. big endian! (handy)
  525. ;; it looks like my adapter uses a revision of the EZUSB that
  526. ;; contains "rev D errata number 8", as hinted in the EzUSB example
  527. ;; code. I cannot find an actual errata description on the Cypress
  528. ;; web site, but from the example code it looks like this bug causes
  529. ;; the length of string descriptors to be read incorrectly, possibly
  530. ;; sending back more characters than the descriptor has. The workaround
  531. ;; is to manually send out all of the data. The consequence of not
  532. ;; using the workaround is that the strings gathered by the kernel
  533. ;; driver are too long and are filled with trailing garbage (including
  534. ;; leftover strings). Writing this out by hand is a nuisance, so for
  535. ;; now I will just live with the bug.
  536. movx a, @dptr
  537. mov r1, a
  538. inc dptr
  539. movx a, @dptr
  540. mov r2, a
  541. mov dptr, SUDPTRH
  542. mov a, r1
  543. movx @dptr, a
  544. mov dptr, SUDPTRL
  545. mov a, r2
  546. movx @dptr, a
  547. ;; done
  548. ljmp setup_done_ack
  549. setup_Get_Descriptor_not_string:
  550. ljmp setup_stall
  551. setup_breq_not_06:
  552. cjne r2, #0x08, setup_breq_not_08
  553. ;; Get_Configuration. always 1. return one byte.
  554. ;; this is reusable
  555. mov a, #1
  556. setup_return_one_byte:
  557. mov dptr, IN0BUF
  558. movx @dptr, a
  559. mov a, #1
  560. mov dptr, IN0BC
  561. movx @dptr, a
  562. ljmp setup_done_ack
  563. setup_breq_not_08:
  564. cjne r2, #0x09, setup_breq_not_09
  565. ;; 09: Set_Configuration. ignored.
  566. ljmp setup_done_ack
  567. setup_breq_not_09:
  568. cjne r2, #0x0a, setup_breq_not_0a
  569. ;; 0a: Get_Interface. get the current altsetting for int[wIndexL]
  570. ;; since we only have one interface, ignore wIndexL, return a 0
  571. mov a, #0
  572. ljmp setup_return_one_byte
  573. setup_breq_not_0a:
  574. cjne r2, #0x0b, setup_breq_not_0b
  575. ;; 0b: Set_Interface. set altsetting for interface[wIndexL]. ignored
  576. ljmp setup_done_ack
  577. setup_breq_not_0b:
  578. ljmp setup_stall
  579. setup_done_ack:
  580. ;; now clear HSNAK
  581. mov dptr, EP0CS
  582. mov a, #0x02
  583. movx @dptr, a
  584. sjmp setup_done
  585. setup_stall:
  586. ;; unhandled. STALL
  587. ;EP0CS |= bmEPSTALL
  588. mov dptr, EP0CS
  589. movx a, @dptr
  590. orl a, EP0STALLbit
  591. movx @dptr, a
  592. sjmp setup_done
  593. setup_done:
  594. pop acc
  595. pop dph1
  596. pop dpl1
  597. pop dph
  598. pop dpl
  599. pop dps
  600. reti
  601. ;;; ==============================================================
  602. set_baud: ; baud index in r3
  603. ;; verify a < 10
  604. mov a, r3
  605. jb ACC.7, set_baud__badbaud
  606. clr c
  607. subb a, #10
  608. jnc set_baud__badbaud
  609. mov a, r3
  610. rl a ; a = index*2
  611. add a, #LOW(baud_table)
  612. mov dpl, a
  613. mov a, #HIGH(baud_table)
  614. addc a, #0
  615. mov dph, a
  616. ;; TODO: shut down xmit/receive
  617. ;; TODO: wait for current xmit char to leave
  618. ;; TODO: shut down timer to avoid partial-char glitch
  619. movx a,@dptr ; BAUD_HIGH
  620. mov RCAP2H, a
  621. mov TH2, a
  622. inc dptr
  623. movx a,@dptr ; BAUD_LOW
  624. mov RCAP2L, a
  625. mov TL2, a
  626. ;; TODO: restart xmit/receive
  627. ;; TODO: reenable interrupts, resume tx if pending
  628. clr c ; c=0: success
  629. ret
  630. set_baud__badbaud:
  631. setb c ; c=1: failure
  632. ret
  633. ;;; ==================================================
  634. control_pins:
  635. cjne r1, #0x41, control_pins_in
  636. control_pins_out:
  637. mov a, r3 ; wValue[0] holds new bits: b7 is new DTR, b2 is new RTS
  638. xrl a, #0xff ; 1 means active, 0V, +12V ?
  639. anl a, #0x84
  640. mov r3, a
  641. mov dptr, OUTC
  642. movx a, @dptr ; only change bits 7 and 2
  643. anl a, #0x7b ; ~0x84
  644. orl a, r3
  645. movx @dptr, a ; other pins are inputs, bits ignored
  646. ljmp setup_done_ack
  647. control_pins_in:
  648. mov dptr, PINSC
  649. movx a, @dptr
  650. xrl a, #0xff
  651. ljmp setup_return_one_byte
  652. ;;; ========================================
  653. ISR_Ep2in:
  654. push dps
  655. push dpl
  656. push dph
  657. push dpl1
  658. push dph1
  659. push acc
  660. mov a,EXIF
  661. clr acc.4
  662. mov EXIF,a ; clear INT2 first
  663. mov dptr, IN07IRQ ; clear USB int
  664. mov a,#04h
  665. movx @dptr,a
  666. ;; do stuff
  667. lcall start_in
  668. pop acc
  669. pop dph1
  670. pop dpl1
  671. pop dph
  672. pop dpl
  673. pop dps
  674. reti
  675. ISR_Ep2out:
  676. push dps
  677. push dpl
  678. push dph
  679. push dpl1
  680. push dph1
  681. push acc
  682. mov a,EXIF
  683. clr acc.4
  684. mov EXIF,a ; clear INT2 first
  685. mov dptr, OUT07IRQ ; clear USB int
  686. mov a,#04h
  687. movx @dptr,a
  688. ;; do stuff
  689. ;; copy data into buffer. for now, assume we will have enough space
  690. mov dptr, OUT2BC ; get byte count
  691. movx a,@dptr
  692. mov r1, a
  693. clr a
  694. mov dps, a
  695. mov dptr, OUT2BUF ; load DPTR0 with source
  696. mov dph1, #HIGH(tx_ring) ; load DPTR1 with target
  697. mov dpl1, tx_ring_in
  698. OUT_loop:
  699. movx a,@dptr ; read
  700. inc dps ; switch to DPTR1: target
  701. inc dpl1 ; target = tx_ring_in+1
  702. movx @dptr,a ; store
  703. mov a,dpl1
  704. cjne a, tx_ring_out, OUT_no_overflow
  705. sjmp OUT_overflow
  706. OUT_no_overflow:
  707. inc tx_ring_in ; tx_ring_in++
  708. inc dps ; switch to DPTR0: source
  709. inc dptr
  710. djnz r1, OUT_loop
  711. sjmp OUT_done
  712. OUT_overflow:
  713. ;; signal overflow
  714. ;; fall through
  715. OUT_done:
  716. ;; ack
  717. mov dptr,OUT2BC
  718. movx @dptr,a
  719. ;; start tx
  720. acall maybe_start_tx
  721. ;acall dump_stat
  722. pop acc
  723. pop dph1
  724. pop dpl1
  725. pop dph
  726. pop dpl
  727. pop dps
  728. reti
  729. dump_stat:
  730. ;; fill in EP4in with a debugging message:
  731. ;; tx_ring_in, tx_ring_out, rx_ring_in, rx_ring_out
  732. ;; tx_active
  733. ;; tx_ring[0..15]
  734. ;; 0xfc
  735. ;; rx_ring[0..15]
  736. clr a
  737. mov dps, a
  738. mov dptr, IN4CS
  739. movx a, @dptr
  740. jb acc.1, dump_stat__done; busy: cannot dump, old one still pending
  741. mov dptr, IN4BUF
  742. mov a, tx_ring_in
  743. movx @dptr, a
  744. inc dptr
  745. mov a, tx_ring_out
  746. movx @dptr, a
  747. inc dptr
  748. mov a, rx_ring_in
  749. movx @dptr, a
  750. inc dptr
  751. mov a, rx_ring_out
  752. movx @dptr, a
  753. inc dptr
  754. clr a
  755. jnb TX_RUNNING, dump_stat__no_tx_running
  756. inc a
  757. dump_stat__no_tx_running:
  758. movx @dptr, a
  759. inc dptr
  760. ;; tx_ring[0..15]
  761. inc dps
  762. mov dptr, #tx_ring ; DPTR1: source
  763. mov r1, #16
  764. dump_stat__tx_ring_loop:
  765. movx a, @dptr
  766. inc dptr
  767. inc dps
  768. movx @dptr, a
  769. inc dptr
  770. inc dps
  771. djnz r1, dump_stat__tx_ring_loop
  772. inc dps
  773. mov a, #0xfc
  774. movx @dptr, a
  775. inc dptr
  776. ;; rx_ring[0..15]
  777. inc dps
  778. mov dptr, #rx_ring ; DPTR1: source
  779. mov r1, #16
  780. dump_stat__rx_ring_loop:
  781. movx a, @dptr
  782. inc dptr
  783. inc dps
  784. movx @dptr, a
  785. inc dptr
  786. inc dps
  787. djnz r1, dump_stat__rx_ring_loop
  788. ;; now send it
  789. clr a
  790. mov dps, a
  791. mov dptr, IN4BC
  792. mov a, #38
  793. movx @dptr, a
  794. dump_stat__done:
  795. ret
  796. ;;; ============================================================
  797. maybe_start_tx:
  798. ;; make sure the tx process is running.
  799. jb TX_RUNNING, start_tx_done
  800. start_tx:
  801. ;; is there work to be done?
  802. mov a, tx_ring_in
  803. cjne a,tx_ring_out, start_tx__work
  804. ret ; no work
  805. start_tx__work:
  806. ;; tx was not running. send the first character, setup the TI int
  807. inc tx_ring_out ; [++tx_ring_out]
  808. mov dph, #HIGH(tx_ring)
  809. mov dpl, tx_ring_out
  810. movx a, @dptr
  811. mov sbuf, a
  812. setb TX_RUNNING
  813. start_tx_done:
  814. ;; can we unthrottle the host tx process?
  815. ;; step 1: do we care?
  816. mov a, #0
  817. cjne a, tx_unthrottle_threshold, start_tx__maybe_unthrottle_tx
  818. ;; nope
  819. start_tx_really_done:
  820. ret
  821. start_tx__maybe_unthrottle_tx:
  822. ;; step 2: is there now room?
  823. mov a, tx_ring_out
  824. setb c
  825. subb a, tx_ring_in
  826. ;; a is now write_room. If thresh >= a, we can unthrottle
  827. clr c
  828. subb a, tx_unthrottle_threshold
  829. jc start_tx_really_done ; nope
  830. ;; yes, we can unthrottle. remove the threshold and mark a request
  831. mov tx_unthrottle_threshold, #0
  832. setb DO_TX_UNTHROTTLE
  833. ;; prod rx, which will actually send the message when in2 becomes free
  834. ljmp start_in
  835. serial_int:
  836. push dps
  837. push dpl
  838. push dph
  839. push dpl1
  840. push dph1
  841. push acc
  842. jnb TI, serial_int__not_tx
  843. ;; tx finished. send another character if we have one
  844. clr TI ; clear int
  845. clr TX_RUNNING
  846. lcall start_tx
  847. serial_int__not_tx:
  848. jnb RI, serial_int__not_rx
  849. lcall get_rx_char
  850. clr RI ; clear int
  851. serial_int__not_rx:
  852. ;; return
  853. pop acc
  854. pop dph1
  855. pop dpl1
  856. pop dph
  857. pop dpl
  858. pop dps
  859. reti
  860. get_rx_char:
  861. mov dph, #HIGH(rx_ring)
  862. mov dpl, rx_ring_in
  863. inc dpl ; target = rx_ring_in+1
  864. mov a, sbuf
  865. movx @dptr, a
  866. ;; check for overflow before incrementing rx_ring_in
  867. mov a, dpl
  868. cjne a, rx_ring_out, get_rx_char__no_overflow
  869. ;; signal overflow
  870. ret
  871. get_rx_char__no_overflow:
  872. inc rx_ring_in
  873. ;; kick off USB INpipe
  874. acall start_in
  875. ret
  876. start_in:
  877. ;; check if the inpipe is already running.
  878. mov dptr, IN2CS
  879. movx a, @dptr
  880. jb acc.1, start_in__done; int will handle it
  881. jb DO_TX_UNTHROTTLE, start_in__do_tx_unthrottle
  882. ;; see if there is any work to do. a serial interrupt might occur
  883. ;; during this sequence?
  884. mov a, rx_ring_in
  885. cjne a, rx_ring_out, start_in__have_work
  886. ret ; nope
  887. start_in__have_work:
  888. ;; now copy as much data as possible into the pipe. 63 bytes max.
  889. clr a
  890. mov dps, a
  891. mov dph, #HIGH(rx_ring) ; load DPTR0 with source
  892. inc dps
  893. mov dptr, IN2BUF ; load DPTR1 with target
  894. movx @dptr, a ; in[0] signals that rest of IN is rx data
  895. inc dptr
  896. inc dps
  897. ;; loop until we run out of data, or we have copied 64 bytes
  898. mov r1, #1 ; INbuf size counter
  899. start_in__loop:
  900. mov a, rx_ring_in
  901. cjne a, rx_ring_out, start_inlocal_irq_enablell_copying
  902. sjmp start_in__kick
  903. start_inlocal_irq_enablell_copying:
  904. inc rx_ring_out
  905. mov dpl, rx_ring_out
  906. movx a, @dptr
  907. inc dps
  908. movx @dptr, a ; write into IN buffer
  909. inc dptr
  910. inc dps
  911. inc r1
  912. cjne r1, #64, start_in__loop; loop
  913. start_in__kick:
  914. ;; either we ran out of data, or we copied 64 bytes. r1 has byte count
  915. ;; kick off IN
  916. mov dptr, IN2BC
  917. mov a, r1
  918. jz start_in__done
  919. movx @dptr, a
  920. ;; done
  921. start_in__done:
  922. ;acall dump_stat
  923. ret
  924. start_in__do_tx_unthrottle:
  925. ;; special sequence: send a tx unthrottle message
  926. clr DO_TX_UNTHROTTLE
  927. clr a
  928. mov dps, a
  929. mov dptr, IN2BUF
  930. mov a, #1
  931. movx @dptr, a
  932. inc dptr
  933. mov a, #2
  934. movx @dptr, a
  935. mov dptr, IN2BC
  936. movx @dptr, a
  937. ret
  938. putchar:
  939. clr TI
  940. mov SBUF, a
  941. putchar_wait:
  942. jnb TI, putchar_wait
  943. clr TI
  944. ret
  945. baud_table: ; baud_high, then baud_low
  946. ;; baud[0]: 110
  947. .byte BAUD_HIGH(110)
  948. .byte BAUD_LOW(110)
  949. ;; baud[1]: 300
  950. .byte BAUD_HIGH(300)
  951. .byte BAUD_LOW(300)
  952. ;; baud[2]: 1200
  953. .byte BAUD_HIGH(1200)
  954. .byte BAUD_LOW(1200)
  955. ;; baud[3]: 2400
  956. .byte BAUD_HIGH(2400)
  957. .byte BAUD_LOW(2400)
  958. ;; baud[4]: 4800
  959. .byte BAUD_HIGH(4800)
  960. .byte BAUD_LOW(4800)
  961. ;; baud[5]: 9600
  962. .byte BAUD_HIGH(9600)
  963. .byte BAUD_LOW(9600)
  964. ;; baud[6]: 19200
  965. .byte BAUD_HIGH(19200)
  966. .byte BAUD_LOW(19200)
  967. ;; baud[7]: 38400
  968. .byte BAUD_HIGH(38400)
  969. .byte BAUD_LOW(38400)
  970. ;; baud[8]: 57600
  971. .byte BAUD_HIGH(57600)
  972. .byte BAUD_LOW(57600)
  973. ;; baud[9]: 115200
  974. .byte BAUD_HIGH(115200)
  975. .byte BAUD_LOW(115200)
  976. desc_device:
  977. .byte 0x12, 0x01, 0x00, 0x01, 0xff, 0xff, 0xff, 0x40
  978. .byte 0xcd, 0x06, 0x04, 0x01, 0x89, 0xab, 1, 2, 3, 0x01
  979. ;;; The "real" device id, which must match the host driver, is that
  980. ;;; "0xcd 0x06 0x04 0x01" sequence, which is 0x06cd, 0x0104
  981. desc_config1:
  982. .byte 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32
  983. .byte 0x09, 0x04, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0x00
  984. .byte 0x07, 0x05, 0x82, 0x03, 0x40, 0x00, 0x01
  985. .byte 0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00
  986. desc_strings:
  987. .word string_langids, string_mfg, string_product, string_serial
  988. desc_strings_end:
  989. string_langids: .byte string_langids_end-string_langids
  990. .byte 3
  991. .word 0
  992. string_langids_end:
  993. ;; sigh. These strings are Unicode, meaning UTF16? 2 bytes each. Now
  994. ;; *that* is a pain in the ass to encode. And they are little-endian
  995. ;; too. Use this perl snippet to get the bytecodes:
  996. /* while (<>) {
  997. @c = split(//);
  998. foreach $c (@c) {
  999. printf("0x%02x, 0x00, ", ord($c));
  1000. }
  1001. }
  1002. */
  1003. string_mfg: .byte string_mfg_end-string_mfg
  1004. .byte 3
  1005. ; .byte "ACME usb widgets"
  1006. .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
  1007. string_mfg_end:
  1008. string_product: .byte string_product_end-string_product
  1009. .byte 3
  1010. ; .byte "ACME USB serial widget"
  1011. .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
  1012. string_product_end:
  1013. string_serial: .byte string_serial_end-string_serial
  1014. .byte 3
  1015. ; .byte "47"
  1016. .byte 0x34, 0x00, 0x37, 0x00
  1017. string_serial_end:
  1018. ;;; ring buffer memory
  1019. ;; tx_ring_in+1 is where the next input byte will go
  1020. ;; [tx_ring_out] has been sent
  1021. ;; if tx_ring_in == tx_ring_out, theres no work to do
  1022. ;; there are (tx_ring_in - tx_ring_out) chars to be written
  1023. ;; dont let _in lap _out
  1024. ;; cannot inc if tx_ring_in+1 == tx_ring_out
  1025. ;; write [tx_ring_in+1] then tx_ring_in++
  1026. ;; if (tx_ring_in+1 == tx_ring_out), overflow
  1027. ;; else tx_ring_in++
  1028. ;; read/send [tx_ring_out+1], then tx_ring_out++
  1029. ;; rx_ring_in works the same way
  1030. .org 0x1000
  1031. tx_ring:
  1032. .skip 0x100 ; 256 bytes
  1033. rx_ring:
  1034. .skip 0x100 ; 256 bytes
  1035. .END