📄 usbch9.c
字号:
ReplyLen = LESSER_OF( sizeof(stringDescProd), wLength );
control_transmit( (const uchar *) &stringDescProd, ReplyLen );
break;
case SERIAL_NUM_STR_ID: /* serial number string */
ReplyLen = LESSER_OF( sizeof(stringDescSNum), wLength );
control_transmit( (const uchar *) &stringDescSNum, ReplyLen );
break;
default:
stall_ep0();
break;
}
}
else stall_ep0();
}
void get_configuration(void)
{
uchar c = USBflags.bits.dev_configured;
single_transmit(&c, 1);
}
void set_configuration(void)
{
if ( ControlData.DeviceRequest.wValue == 0 ) /* put device in unconfigured state */
{
single_transmit(0, 0); /* Ack */
DISABLE_USB_IRQ;
USBflags.bits.dev_configured = FALSE;
RESTORE_USB_IRQ;
unconfigure_device();
init_rem_loc_state();
}
else if ( ControlData.DeviceRequest.wValue == 1 ) /* put device in configured state */
{
single_transmit(0, 0); /* Ack */
unconfigure_device();
configure_device();
DISABLE_USB_IRQ;
USBflags.bits.dev_configured = TRUE;
RESTORE_USB_IRQ;
init_rem_loc_state();
}
else stall_ep0();
}
void get_interface(void)
{
uchar txdat = 0; /* Current (sole) interface number = 0x00 */
single_transmit( &txdat, 1 );
}
void set_interface(void)
{
if ( ControlData.DeviceRequest.wValue == 0 && ControlData.DeviceRequest.wIndex == 0 )
single_transmit( 0, 0 ); /* Ack */
else
stall_ep0();
}
/* Called if undefined Standard Device Request is received */
void req_undefined( void )
{
stall_ep0();
debug_signal( BIT_14 );
}
/************************************************************************** */
/* Utility functions req'd by USB2.0 Chapter 9 and USBTMC class */
/************************************************************************** */
static ulong usb_reconnect_time;
static bool usb_reconnecting = FALSE;
void stall_ep0(void)
{
D12_SetEndpointStatus( 0, 1 );
D12_SetEndpointStatus( 1, 1 );
}
/* Low-level D12 chip initialisation -- does not affect system IRQ status */
void USB_init(void)
{
d12outp(D12_COMMAND, 0xF3); /* Set Mode (detach from Vbus) */
d12outp(D12_DATA, D12_CLOCKRUNNING|D12_NOLAZYCLOCK);
d12outp(D12_DATA, D12_SETTOONE|D12_CLOCK_4M);
d12outp(D12_COMMAND, 0xF4); /* Read Int. reg (to clear power-on IRQ) */
d12inp(D12_DATA);
d12inp(D12_DATA);
d12outp(D12_COMMAND, 0xFB); /* Set DMA mode (off) */
d12outp(D12_DATA, 0);
}
/* Initialize D12 configuration - detach from Vbus (SoftConnect = 0) */
void disconnect_USB(void)
{
DISABLE_USB_IRQ;
D12_SetMode( D12_CLOCKRUNNING|D12_NOLAZYCLOCK, D12_SETTOONE|D12_CLOCK_4M );
D12_ReadInterruptRegister(); /* Clear 'bus reset' IRQ flag */
D12_SetDMA( 0 );
USB_active = FALSE;
RESTORE_USB_IRQ;
}
/* Initialize D12 configuration - attach to Vbus (SoftConnect = 1) */
void connect_USB(void)
{
DISABLE_USB_IRQ;
USBflags.value = 0; /* Clear USB state flags */
D12_SetMode( D12_SOFTCONNECT|D12_CLOCKRUNNING|D12_NOLAZYCLOCK, D12_SETTOONE|D12_CLOCK_4M );
D12_SetDMA( D12_ENDP4INTENABLE|D12_ENDP5INTENABLE ); /* Allow EP2 Tx and Rx interrupts */
D12_SetAddressEnable( 0, 1 ); /* Give D12 default addr (0) and enable it */
USB_active = TRUE;
RESTORE_USB_IRQ;
}
/*
* This function requires a timer of some kind, to produce a one-second (approx.) wait.
* The timer function used here, get_tick_count(), returns the value of a 16-bit
* free-running counter. One count (tick) is 5 milli-seconds.
* It's probably better not to use an interrupt-driven timer for this purpose, because
* system IRQ's may not be enabled when reconnect_USB() is called.
*/
void reconnect_USB(void)
{
ulong startCount = get_tick_count();
disconnect_USB();
while( get_tick_count() - startCount < 100 ) /* @ 100 ticks per sec */
{
;;; /* Wait a second... */
}
connect_USB();
usb_reconnect_time = get_tick_count(); /* Start 10 sec timeout for reconnect */
usb_reconnecting = TRUE; /* see maintain_usb_connection() */
}
/*
* This function is called frequently from the main backround loop (in main.c) to
* ensure connection with the host is re-established following a bus reset.
* The 'bus reset' flag (set by USB ISR) is polled; if it is set (true) and the
* time elapsed since the last re-connect attempt is at least 4 seconds, then
* the function will disconnect the device from the bus and set a 1 second disconnect
* timer. When the 1 sec timer expires, the function reconnects the device to the
* bus, allowing the host to attempt enumeration. A 4 second "hold-off" timer is
* started on reconnection. During the hold-off interval, the function ignores any
* further bus reset requests; it simply clears the 'bus reset' flag.
*
* Note: This function can interfere with the host driver installation process
* if the device is malfunctioning, e.g. not enumerating correctly.
* It might be necessary to disable calls to the function during firmware development.
* Enabling the function is optional, but recommended for commercial applications.
*/
void maintain_usb_connection( void )
{
static ulong usb_disconnect_time;
static bool usb_disconnecting = FALSE;
if ( USBflags.bits.bus_reset )
{
USBflags.bits.bus_reset = FALSE;
if ( !usb_reconnecting && !usb_disconnecting )
{
/* Ready to respond to bus reset req... */
disconnect_USB();
usb_disconnect_time = get_tick_count(); /* start 1 sec timeout */
usb_disconnecting = TRUE;
}
}
if ( usb_disconnecting )
{
if ( get_tick_count() - usb_disconnect_time >= 100 ) /* 1 sec elapsed */
{
usb_disconnecting = FALSE;
connect_USB();
usb_reconnect_time = get_tick_count(); /* start 4 sec hold-off */
usb_reconnecting = TRUE;
}
}
else if ( usb_reconnecting )
{
if ( get_tick_count() - usb_reconnect_time >= 400 ) /* 4 sec elapsed */
usb_reconnecting = FALSE;
}
}
void unconfigure_device(void)
{
D12_SetEndpointEnable( 0 ); /* Disable all endpoints but EP0. */
}
void configure_device(void)
{
D12_SetEndpointEnable( 1 ); /* Enable all endpoints. */
}
/*
* Function writes data into the D12 control endpoint FIFO for transmission.
* This initiates transmission of a single-packet Control-In transfer.
* It is assumed that the D12 EP0 TX FIFO has sufficient room to accommodate the new data.
* The amount of data to be transmitted must be <= EP0_MAX_PACKET_SIZE.
*
* Entry args: (uchar *) buf = pointer to source data
* (ushort) len = number of chars to write (max. EP0_MAX_PACKET_SIZE)
* Returns: --
* Affects: --
*/
void single_transmit( uchar * buf, uchar len )
{
if ( len <= EP0_MAX_PACKET_SIZE )
{
D12_WriteEndpoint( 1, buf, len ); /* EP index is 1 => Ctrl-IN (to host) */
}
}
/*
* Function writes 'const' data into the D12 control endpoint buffer.
* This initiates transmission of the first (or only) packet of a Control-In transfer.
* It is assumed that the D12 EP0 TX FIFO has sufficient room to accommodate the new data.
* If the amount of data to be transferred (ControlData.wLength) exceeds or equals
* EP0_MAX_PACKET_SIZE, remaining data will be transmitted by control_in_handler() on subsequent
* EP0 TX Request interrupts. (usbisr.c) ***Final packet must be short pkt or null pkt.***
*
* Entry args: (const uchar *) pSrcData = pointer to source data
* (ushort) len = maximum number of bytes to be transferred
* Returns: --
* Affects: ControlData.wCount, ControlData.pData, ControlData.wLength
*
* Note: ControlData.wLength = transfer size (bytes) as received in the last
* device request (Ctrl Out) pkt. ControlData.wLength is capped at len.
*
*/
void control_transmit( const uchar * pSrcData, ushort len )
{
ControlData.wCount = 0; /* Count of bytes sent so far */
ControlData.pData = (uchar *) pSrcData; /* Pointer to source data */
if ( ControlData.wLength > len ) ControlData.wLength = len;
if ( ControlData.wLength >= EP0_MAX_PACKET_SIZE ) /* More than 1 pkt to send */
{
D12_WriteEndpoint( 1, ControlData.pData, EP0_MAX_PACKET_SIZE );
ControlData.wCount += EP0_MAX_PACKET_SIZE;
DISABLE_USB_IRQ;
USBflags.bits.control_state = USB_TRANSMIT;
RESTORE_USB_IRQ;
}
else /* Only 1 packet to send */
{
D12_WriteEndpoint( 1, (uchar *) pSrcData, ControlData.wLength );
ControlData.wCount += ControlData.wLength;
DISABLE_USB_IRQ;
USBflags.bits.control_state = USB_IDLE;
RESTORE_USB_IRQ;
}
}
/*****
* Convert Little-endian number (32 bits unsigned, stored as 4 byte array, LSB first) to
* ulong integer (32 bits).
*
* Entry args: (uchar *) pSource = pointer to LSB of Little-endian number
*
* Returns: (ulong) value of the number stored at pSource
* Affects: --
*/
ulong LittleEndianToLong( uchar * pSource )
{
ulong result;
result = (ulong) *pSource++;
result += (ulong) *pSource++ * 256;
result += (ulong) *pSource++ * 256;
result += (ulong) *pSource++ * 256;
return result;
}
/*****
* Convert big-endian variable (ulong integer) to Little-endian number, 32 bits,
* stored as 4 byte array, LSB first.
*
* Entry args: (uchar *) pDestin = pointer to LSB of Little-endian result
* (ulong) value = number to be converted
* Returns: --
* Affects: --
*/
void toLittleEndian( ulong value, uchar * pDestin )
{
short i;
uchar * pResult = pDestin; /* point to LSB of result */
for ( i = 0; i < 4; i++ )
{
*pResult++ = (uchar) (value & 0xFF);
value = value >> 8;
}
}
/* end */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -