xircom_pgs.S 29 KB

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