⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 xircom_pgs.s

📁 基于S3CEB2410平台LINUX操作系统下 USB驱动源代码
💻 S
📖 第 1 页 / 共 2 页
字号:
/*  $Id: loop.s,v 1.23 2000/03/20 09:49:06 warner Exp $ *  *  Firmware for the Keyspan PDA Serial Adapter, a USB serial port based on *  the EzUSB microcontroller. *  *  (C) Copyright 2000 Brian Warner <warner@lothar.com> *  * 	This program is free software; you can redistribute it and/or modify * 	it under the terms of the GNU General Public License as published by * 	the Free Software Foundation; either version 2 of the License, or * 	(at your option) any later version. *  *  "Keyspan PDA Serial Adapter" is probably a copyright of Keyspan, the *  company. *  *  This serial adapter is basically an EzUSB chip and an RS-232 line driver *  in a little widget that has a DB-9 on one end and a USB plug on the other. *  It uses the EzUSB's internal UART0 (using the pins from Port C) and timer2 *  as a baud-rate generator. The wiring is: *   PC0/RxD0 <- rxd (DB9 pin 2)         PC4 <- dsr pin 6 *   PC1/TxD0 -> txd pin 3               PC5 <- ri  pin 9 *   PC2      -> rts pin 7               PC6 <- dcd pin 1 *   PC3      <- cts pin 8               PC7 -> dtr pin 4 *   PB1 -> line driver standby * *  The EzUSB register constants below come from their excellent documentation *  and sample code (which used to be available at www.anchorchips.com, but *  that has now been absorbed into Cypress' site and the CD-ROM contents *  don't appear to be available online anymore). If we get multiple *  EzUSB-based drivers into the kernel, it might be useful to pull them out *  into a separate .h file. *  * THEORY OF OPERATION: * *   There are two 256-byte ring buffers, one for tx, one for rx. * *   EP2out is pure tx data. When it appears, the data is copied into the tx *   ring and serial transmission is started if it wasn't already running. The *   "tx buffer empty" interrupt may kick off another character if the ring *   still has data. If the host is tx-blocked because the ring filled up, *   it will request a "tx unthrottle" interrupt. If sending a serial character *   empties the ring below the desired threshold, we set a bit that will send *   up the tx unthrottle message as soon as the rx buffer becomes free. * *   EP2in (interrupt) is used to send both rx chars and rx status messages *   (only "tx unthrottle" at this time) back up to the host. The first byte *   of the rx message indicates data (0) or status msg (1). Status messages *   are sent before any data. * *   Incoming serial characters are put into the rx ring by the serial *   interrupt, and the EP2in buffer sent if it wasn't already in transit. *   When the EP2in buffer returns, the interrupt prompts us to send more *   rx chars (or status messages) if they are pending. * *   Device control happens through "vendor specific" control messages on EP0. *   All messages are destined for the "Interface" (with the index always 0, *   so that if their two-port device might someday use similar firmware, we *   can use index=1 to refer to the second port). The messages defined are: * *    bRequest = 0 : set baud/bits/parity *               1 : unused *               2 : reserved for setting HW flow control (CTSRTS) *               3 : get/set "modem info" (pin states: DTR, RTS, DCD, RI, etc) *               4 : set break (on/off) *               5 : reserved for requesting interrupts on pin state change *               6 : query buffer room or chars in tx buffer *               7 : request tx unthrottle interrupt * *  The host-side driver is set to recognize the device ID values stashed in *  serial EEPROM (0x06cd, 0x0103), program this firmware into place, then *  start it running. This firmware will use EzUSB's "renumeration" trick by *  simulating a bus disconnect, then reconnect with a different device ID *  (encoded in the desc_device descriptor below). The host driver then *  recognizes the new device ID and glues it to the real serial driver code. * * USEFUL DOCS: *  EzUSB Technical Reference Manual: <http://www.anchorchips.com> *  8051 manuals: everywhere, but try www.dalsemi.com because the EzUSB is *   basically the Dallas enhanced 8051 code. Remember that the EzUSB IO ports *   use totally different registers! *  USB 1.1 spec: www.usb.org * * HOW TO BUILD: *  gcc -x assembler-with-cpp -P -E -o keyspan_pda.asm keyspan_pda.s *  as31 -l keyspan_pda.asm *  mv keyspan_pda.obj keyspan_pda.hex *  perl ezusb_convert.pl keyspan_pda < keyspan_pda.hex > keyspan_pda_fw.h * Get as31 from <http://www.pjrc.com/tech/8051/index.html>, and hack on it * a bit to make it build. * * THANKS: *  Greg Kroah-Hartman, for coordinating the whole usb-serial thing. *  AnchorChips, for making such an incredibly useful little microcontroller. *  KeySpan, for making a handy, cheap ($40) widget that was so easy to take *           apart and trace with an ohmmeter. * * TODO: *  lots. grep for TODO. Interrupt safety needs stress-testing. Better flow *  control. Interrupting host upon change in DCD, etc, counting transitions. *  Need to find a safe device id to use (the one used by the Keyspan firmware *  under Windows would be ideal.. can anyone figure out what it is?). Parity. *  More baud rates. Oh, and the string-descriptor-length silicon bug *  workaround should be implemented, but I'm lazy, and the consequence is *  that the device name strings that show up in your kernel log will have *  lots of trailing binary garbage in them (appears as ????). Device strings *  should be made more accurate. * * Questions, bugs, patches to Brian. * *  -Brian Warner <warner@lothar.com> * */	#define HIGH(x) (((x) & 0xff00) / 256)#define LOW(x) ((x) & 0xff)#define dpl1 0x84#define dph1 0x85#define dps 0x86;;; our bit assignments#define TX_RUNNING 0#define DO_TX_UNTHROTTLE 1		;; stack from 0x60 to 0x7f: should really set SP to 0x60-1, not 0x60#define STACK #0x60-1#define EXIF 0x91#define EIE 0xe8	.flag EUSB, EIE.0	.flag ES0, IE.4#define EP0CS #0x7fb4#define EP0STALLbit #0x01#define IN0BUF #0x7f00#define IN0BC #0x7fb5#define OUT0BUF #0x7ec0#define OUT0BC #0x7fc5		#define IN2BUF #0x7e00#define IN2BC #0x7fb9#define IN2CS #0x7fb8#define OUT2BC #0x7fc9#define OUT2CS #0x7fc8#define OUT2BUF #0x7dc0#define IN4BUF #0x7d00#define IN4BC #0x7fbd#define IN4CS #0x7fbc#define OEB #0x7f9d#define OUTB #0x7f97#define OEC #0x7f9e#define OUTC #0x7f98#define PINSC #0x7f9b#define PORTBCFG #0x7f94#define PORTCCFG #0x7f95#define OEA	#0x7f9c#define IN07IRQ #0x7fa9#define OUT07IRQ #0x7faa#define IN07IEN #0x7fac#define OUT07IEN #0x7fad#define USBIRQ #0x7fab#define USBIEN #0x7fae#define USBBAV #0x7faf#define USBCS #0x7fd6#define SUDPTRH #0x7fd4#define SUDPTRL #0x7fd5#define SETUPDAT #0x7fe8			;; usb interrupt : enable is EIE.0 (0xe8), flag is EXIF.4 (0x91)	.org 0	ljmp start	;; interrupt vectors	.org 23H	ljmp serial_int	.byte 0		.org 43H	ljmp USB_Jump_Table	.byte 0			; filled in by the USB core;;; local variables. These are not initialized properly: do it by hand.	.org 30Hrx_ring_in:	.byte 0rx_ring_out:	.byte 0tx_ring_in:	.byte 0tx_ring_out:	.byte 0tx_unthrottle_threshold:	.byte 0			.org 0x100H		; wants to be on a page boundaryUSB_Jump_Table:	ljmp	ISR_Sudav	; Setup Data Available	.byte 0	ljmp	0		; Start of Frame	.byte 0	ljmp	0		; Setup Data Loading	.byte 0	ljmp	0		; Global Suspend	.byte 	0	ljmp	0		; USB Reset  		.byte	0	ljmp	0		; Reserved	.byte	0	ljmp	0		; End Point 0 In	.byte	0	ljmp	0		; End Point 0 Out	.byte	0	ljmp	0		; End Point 1 In	.byte	0	ljmp	0		; End Point 1 Out	.byte	0	ljmp	ISR_Ep2in	.byte	0	ljmp	ISR_Ep2out	.byte	0	.org 0x200		start:	mov SP,STACK-1 ; set stack	;; clear local variables	clr a	mov tx_ring_in, a	mov tx_ring_out, a	mov rx_ring_in, a	mov rx_ring_out, a	mov tx_unthrottle_threshold, a	clr TX_RUNNING	clr DO_TX_UNTHROTTLE		;; clear fifo with "fe"	mov r1, 0	mov a, #0xfe	mov dptr, #tx_ringclear_tx_ring_loop:	movx @dptr, a	inc dptr	djnz r1, clear_tx_ring_loop	mov a, #0xfd	mov dptr, #rx_ringclear_rx_ring_loop:	movx @dptr, a	inc dptr	djnz r1, clear_rx_ring_loop;;; turn on the RS-232 driver chip (bring the STANDBY pin low);;; on Xircom the STANDBY is wired to PB6 and PC4 	mov dptr, PORTBCFG        mov a, #0xBf        movx @dptr, a	mov dptr, PORTCCFG        mov a, #0xef        movx @dptr, a		;; set OEC.4        mov a, #0x10        mov dptr,OEC        movx @dptr,a        ;; clear PC4        mov a, #0x00        mov dptr,OUTC        movx @dptr,a	;; set OEB.6	mov a, #0x40	mov dptr,OEB	movx @dptr,a	;; clear PB6	mov a, #0x00	mov dptr,OUTB	movx @dptr,a	;; set OEC.[17]	mov a, #0x82	mov dptr,OEC	movx @dptr,a	;; set PORTCCFG.[01] to route TxD0,RxD0 to serial port	mov dptr, PORTCCFG	mov a, #0x03	movx @dptr, a		;; set up interrupts, autovectoring	;; set BKPT	mov dptr, USBBAV	movx a,@dptr	setb acc.0		; AVEN bit to 0	movx @dptr, a	mov a,#0x01		; enable SUDAV:	setup data available (for ep0)	mov dptr, USBIRQ	movx @dptr, a		; clear SUDAVI	mov dptr, USBIEN	movx @dptr, a		mov dptr, IN07IEN	mov a,#0x04		; enable IN2 int	movx @dptr, a		mov dptr, OUT07IEN	mov a,#0x04		; enable OUT2 int	movx @dptr, a	mov dptr, OUT2BC	movx @dptr, a		; arm OUT2;;	mov a, #0x84		; turn on RTS, DTR;;	mov dptr,OUTC;;	movx @dptr, a	mov a, #0x7             ; turn on  DTR        mov dptr,USBBAV        movx @dptr, a	mov a, #0x20             ; turn on the RED led         mov dptr,OEA        movx @dptr, a	mov a, #0x80            ; turn on  RTS        mov dptr,OUTC        movx @dptr, a	;; setup the serial port. 9600 8N1.	mov a,#0x53		; mode 1, enable rx, clear int	mov SCON, a	;;  using timer2, in 16-bit baud-rate-generator mode	;;   (xtal 12MHz, internal fosc 24MHz)	;;  RCAP2H,RCAP2L = 65536 - fosc/(32*baud)	;;  57600: 0xFFF2.F, say 0xFFF3	;;   9600: 0xFFB1.E, say 0xFFB2	;;    300: 0xF63C#define BAUD 9600#define BAUD_TIMEOUT(rate) (65536 - (24 * 1000 * 1000) / (32 * rate))#define BAUD_HIGH(rate) HIGH(BAUD_TIMEOUT(rate))#define BAUD_LOW(rate) LOW(BAUD_TIMEOUT(rate))			mov T2CON, #030h	; rclk=1,tclk=1,cp=0,tr2=0(enable later)	mov r3, #5	acall set_baud	setb TR2	mov SCON, #050h	#if 0	mov r1, #0x40	mov a, #0x41send:		mov SBUF, a	inc a	anl a, #0x3F	orl a, #0x40;	xrl a, #0x02wait1:		jnb TI, wait1	clr TI	djnz r1, send;done:	sjmp done#endif		setb EUSB	setb EA	setb ES0	;acall dump_stat	;; hey, what say we RENUMERATE! (TRM p.62)	mov a, #0	mov dps, a	mov dptr, USBCS	mov a, #0x02		; DISCON=0, DISCOE=0, RENUM=1	movx @dptr, a	;; now presence pin is floating, simulating disconnect. wait 0.5s	mov r1, #46renum_wait1:	mov r2, #0renum_wait2:	mov r3, #0renum_wait3:	djnz r3, renum_wait3	djnz r2, renum_wait2	djnz r1, renum_wait1	; wait about n*(256^2) 6MHz clocks	mov a, #0x06		; DISCON=0, DISCOE=1, RENUM=1	movx @dptr, a	;; we are back online. the host device will now re-query us		main:	sjmp main	ISR_Sudav:	push dps	push dpl	push dph	push dpl1	push dph1	push acc	mov a,EXIF	clr acc.4	mov EXIF,a		; clear INT2 first	mov dptr, USBIRQ	; clear USB int	mov a,#01h	movx @dptr,a	;; get request type	mov dptr, SETUPDAT	movx a, @dptr	mov r1, a		; r1 = bmRequestType	inc dptr	movx a, @dptr	mov r2, a		; r2 = bRequest	inc dptr	movx a, @dptr	mov r3, a		; r3 = wValueL	inc dptr	movx a, @dptr	mov r4, a		; r4 = wValueH	;; main switch on bmRequest.type: standard or vendor	mov a, r1	anl a, #0x60	cjne a, #0x00, setup_bmreq_type_not_standard	;; standard request: now main switch is on bRequest	ljmp setup_bmreq_is_standard	setup_bmreq_type_not_standard:		;; a still has bmreq&0x60	cjne a, #0x40, setup_bmreq_type_not_vendor	;; Anchor reserves bRequest 0xa0-0xaf, we use small ones	;; switch on bRequest. bmRequest will always be 0x41 or 0xc1	cjne r2, #0x00, setup_ctrl_not_00	;; 00 is set baud, wValue[0] has baud rate index	lcall set_baud		; index in r3, carry set if error	jc setup_bmreq_type_not_standard__do_stall	ljmp setup_done_acksetup_bmreq_type_not_standard__do_stall:	ljmp setup_stallsetup_ctrl_not_00:	cjne r2, #0x01, setup_ctrl_not_01	;; 01 is reserved for set bits (parity). TODO	ljmp setup_stallsetup_ctrl_not_01:	cjne r2, #0x02, setup_ctrl_not_02	;; 02 is set HW flow control. TODO	ljmp setup_stallsetup_ctrl_not_02:	cjne r2, #0x03, setup_ctrl_not_03	;; 03 is control pins (RTS, DTR).	ljmp control_pins	; will jump to setup_done_ack,				;  or setup_return_one_bytesetup_ctrl_not_03:	cjne r2, #0x04, setup_ctrl_not_04	;; 04 is send break (really "turn break on/off"). TODO	cjne r3, #0x00, setup_ctrl_do_break_on	;; do break off: restore PORTCCFG.1 to reconnect TxD0 to serial port	mov dptr, PORTCCFG	movx a, @dptr	orl a, #0x02	movx @dptr, a	ljmp setup_done_acksetup_ctrl_do_break_on:	;; do break on: clear PORTCCFG.0, set TxD high(?) (b1 low)	mov dptr, OUTC	movx a, @dptr	anl a, #0xfd		; ~0x02	movx @dptr, a	mov dptr, PORTCCFG	movx a, @dptr	anl a, #0xfd		; ~0x02	movx @dptr, a	ljmp setup_done_acksetup_ctrl_not_04:	cjne r2, #0x05, setup_ctrl_not_05	;; 05 is set desired interrupt bitmap. TODO	ljmp setup_stallsetup_ctrl_not_05:	cjne r2, #0x06, setup_ctrl_not_06	;; 06 is query room	cjne r3, #0x00, setup_ctrl_06_not_00	;; 06, wValue[0]=0 is query write_room	mov a, tx_ring_out	setb c	subb a, tx_ring_in	; out-1-in = 255 - (in-out)	ljmp setup_return_one_bytesetup_ctrl_06_not_00:	cjne r3, #0x01, setup_ctrl_06_not_01	;; 06, wValue[0]=1 is query chars_in_buffer	mov a, tx_ring_in	clr c	subb a, tx_ring_out	; in-out	ljmp setup_return_one_bytesetup_ctrl_06_not_01:		ljmp setup_stallsetup_ctrl_not_06:	cjne r2, #0x07, setup_ctrl_not_07	;; 07 is request tx unthrottle interrupt	mov tx_unthrottle_threshold, r3; wValue[0] is threshold value	ljmp setup_done_acksetup_ctrl_not_07:	ljmp setup_stall	setup_bmreq_type_not_vendor:	ljmp setup_stallsetup_bmreq_is_standard:		cjne r2, #0x00, setup_breq_not_00	;; 00:	Get_Status (sub-switch on bmRequestType: device, ep, int)	cjne r1, #0x80, setup_Get_Status_not_device	;; Get_Status(device)	;;  are we self-powered? no. can we do remote wakeup? no	;;   so return two zero bytes. This is reusablesetup_return_two_zero_bytes:	mov dptr, IN0BUF	clr a	movx @dptr, a	inc dptr	movx @dptr, a	mov dptr, IN0BC	mov a, #2	movx @dptr, a	ljmp setup_done_acksetup_Get_Status_not_device:	cjne r1, #0x82, setup_Get_Status_not_endpoint	;; Get_Status(endpoint)	;;  must get stall bit for ep[wIndexL], return two bytes, bit in lsb 0	;; for now: cheat. TODO	sjmp setup_return_two_zero_bytessetup_Get_Status_not_endpoint:	cjne r1, #0x81, setup_Get_Status_not_interface	;; Get_Status(interface): return two zeros	sjmp setup_return_two_zero_bytessetup_Get_Status_not_interface:		ljmp setup_stall	setup_breq_not_00:	cjne r2, #0x01, setup_breq_not_01	;; 01:	Clear_Feature (sub-switch on wValueL: stall, remote wakeup)	cjne r3, #0x00, setup_Clear_Feature_not_stall	;; Clear_Feature(stall). should clear a stall bit. TODO	ljmp setup_stallsetup_Clear_Feature_not_stall:	cjne r3, #0x01, setup_Clear_Feature_not_rwake	;; Clear_Feature(remote wakeup). ignored.	ljmp setup_done_acksetup_Clear_Feature_not_rwake:	ljmp setup_stall	setup_breq_not_01:	cjne r2, #0x03, setup_breq_not_03	;; 03:	Set_Feature (sub-switch on wValueL: stall, remote wakeup)	cjne r3, #0x00, setup_Set_Feature_not_stall	;; Set_Feature(stall). Should set a stall bit. TODO	ljmp setup_stallsetup_Set_Feature_not_stall:	cjne r3, #0x01, setup_Set_Feature_not_rwake	;; Set_Feature(remote wakeup). ignored.	ljmp setup_done_acksetup_Set_Feature_not_rwake:	ljmp setup_stall	setup_breq_not_03:		cjne r2, #0x06, setup_breq_not_06	;; 06:	Get_Descriptor (s-switch on wValueH: dev, config[n], string[n])	cjne r4, #0x01, setup_Get_Descriptor_not_device	;; Get_Descriptor(device)	mov dptr, SUDPTRH	mov a, #HIGH(desc_device)	movx @dptr, a	mov dptr, SUDPTRL	mov a, #LOW(desc_device)	movx @dptr, a	ljmp setup_done_acksetup_Get_Descriptor_not_device:	cjne r4, #0x02, setup_Get_Descriptor_not_config	;; Get_Descriptor(config[n])	cjne r3, #0x00, setup_stall; only handle n==0	;; Get_Descriptor(config[0])	mov dptr, SUDPTRH	mov a, #HIGH(desc_config1)	movx @dptr, a	mov dptr, SUDPTRL	mov a, #LOW(desc_config1)	movx @dptr, a	ljmp setup_done_acksetup_Get_Descriptor_not_config:	cjne r4, #0x03, setup_Get_Descriptor_not_string	;; Get_Descriptor(string[wValueL])	;;  if (wValueL >= maxstrings) stall	mov a, #((desc_strings_end-desc_strings)/2)	clr c	subb a,r3		; a=4, r3 = 0..3 . if a<=0 then stall	jc  setup_stall	jz  setup_stall	mov a, r3	add a, r3		; a = 2*wValueL

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -