Skip to content

Commit fbcf948

Browse files
nospam2000obra
authored andcommitted
Add support for waking up a host via USB HID.
This functionality originated in pull request: arduino#1488 Jesse can attest to the functionality (tested on OS X 10.8, 10.9, and Windows 7 but doesn't have the background to verify the implementation
1 parent 4388bed commit fbcf948

File tree

4 files changed

+163
-30
lines changed

4 files changed

+163
-30
lines changed

hardware/arduino/avr/cores/arduino/HID.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
/* Copyright (c) 2011, Peter Barrett
44
**
5+
** Sleep/Wakeup/SystemControl support added by Michael Dreher
6+
**
57
** Permission to use, copy, modify, and/or distribute this software for
68
** any purpose with or without fee is hereby granted, provided that the
79
** above copyright notice and this permission notice appear in all copies.

hardware/arduino/avr/cores/arduino/USBAPI.h

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class USBDevice_
5050
void attach();
5151
void detach(); // Serial port goes down too...
5252
void poll();
53+
bool wakeupHost(); // returns false, when wakeup cannot be processed
5354
};
5455
extern USBDevice_ USBDevice;
5556

hardware/arduino/avr/cores/arduino/USBCore.cpp

+150-29
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
/* Copyright (c) 2010, Peter Barrett
44
**
5+
** Sleep/Wakeup/SystemControl support added by Michael Dreher
6+
**
57
** Permission to use, copy, modify, and/or distribute this software for
68
** any purpose with or without fee is hereby granted, provided that the
79
** above copyright notice and this permission notice appear in all copies.
@@ -20,13 +22,13 @@
2022

2123
#if defined(USBCON)
2224

23-
#define EP_TYPE_CONTROL 0x00
24-
#define EP_TYPE_BULK_IN 0x81
25-
#define EP_TYPE_BULK_OUT 0x80
26-
#define EP_TYPE_INTERRUPT_IN 0xC1
27-
#define EP_TYPE_INTERRUPT_OUT 0xC0
28-
#define EP_TYPE_ISOCHRONOUS_IN 0x41
29-
#define EP_TYPE_ISOCHRONOUS_OUT 0x40
25+
#define EP_TYPE_CONTROL (0x00)
26+
#define EP_TYPE_BULK_IN ((1<<EPTYPE1) | (1<<EPDIR))
27+
#define EP_TYPE_BULK_OUT (1<<EPTYPE1)
28+
#define EP_TYPE_INTERRUPT_IN ((1<<EPTYPE1) | (1<<EPTYPE0) | (1<<EPDIR))
29+
#define EP_TYPE_INTERRUPT_OUT ((1<<EPTYPE1) | (1<<EPTYPE0))
30+
#define EP_TYPE_ISOCHRONOUS_IN ((1<<EPTYPE0) | (1<<EPDIR))
31+
#define EP_TYPE_ISOCHRONOUS_OUT (1<<EPTYPE0)
3032

3133
/** Pulse generation counters to keep track of the number of milliseconds remaining for each pulse type */
3234
#define TX_RX_LED_PULSE_MS 100
@@ -89,6 +91,8 @@ const DeviceDescriptor USB_DeviceDescriptorA =
8991
//==================================================================
9092

9193
volatile u8 _usbConfiguration = 0;
94+
volatile u8 _usbCurrentStatus = 0; // meaning of bits see usb_20.pdf, Figure 9-4. Information Returned by a GetStatus() Request to a Device
95+
volatile u8 _usbSuspendState = 0; // copy of UDINT to check SUSPI and WAKEUPI bits
9296

9397
static inline void WaitIN(void)
9498
{
@@ -340,7 +344,7 @@ static
340344
void InitEP(u8 index, u8 type, u8 size)
341345
{
342346
UENUM = index;
343-
UECONX = 1;
347+
UECONX = (1<<EPEN);
344348
UECFG0X = type;
345349
UECFG1X = size;
346350
}
@@ -351,7 +355,7 @@ void InitEndpoints()
351355
for (u8 i = 1; i < sizeof(_initEndpoints); i++)
352356
{
353357
UENUM = i;
354-
UECONX = 1;
358+
UECONX = (1<<EPEN);
355359
UECFG0X = pgm_read_byte(_initEndpoints+i);
356360
UECFG1X = EP_DOUBLE_64;
357361
}
@@ -544,16 +548,37 @@ ISR(USB_COM_vect)
544548
{
545549
// Standard Requests
546550
u8 r = setup.bRequest;
551+
u16 wValue = setup.wValueL | (setup.wValueH << 8);
547552
if (GET_STATUS == r)
548553
{
549-
Send8(0); // TODO
550-
Send8(0);
554+
if(requestType == (REQUEST_DEVICETOHOST | REQUEST_STANDARD | REQUEST_DEVICE))
555+
{
556+
Send8(_usbCurrentStatus);
557+
Send8(0);
558+
}
559+
else
560+
{
561+
// TODO: handle the HALT state of an endpoint here
562+
// see "Figure 9-6. Information Returned by a GetStatus() Request to an Endpoint" in usb_20.pdf for more information
563+
Send8(0);
564+
Send8(0);
565+
}
551566
}
552567
else if (CLEAR_FEATURE == r)
553568
{
569+
if((requestType == (REQUEST_HOSTTODEVICE | REQUEST_STANDARD | REQUEST_DEVICE))
570+
&& (wValue == DEVICE_REMOTE_WAKEUP))
571+
{
572+
_usbCurrentStatus &= ~FEATURE_REMOTE_WAKEUP_ENABLED;
573+
}
554574
}
555575
else if (SET_FEATURE == r)
556576
{
577+
if((requestType == (REQUEST_HOSTTODEVICE | REQUEST_STANDARD | REQUEST_DEVICE))
578+
&& (wValue == DEVICE_REMOTE_WAKEUP))
579+
{
580+
_usbCurrentStatus |= FEATURE_REMOTE_WAKEUP_ENABLED;
581+
}
557582
}
558583
else if (SET_ADDRESS == r)
559584
{
@@ -609,11 +634,74 @@ void USB_Flush(u8 ep)
609634
ReleaseTX();
610635
}
611636

637+
static inline void USB_ClockDisable()
638+
{
639+
USBCON = (USBCON & ~(1<<OTGPADE)) | (1<<FRZCLK); // freeze clock and disable VBUS Pad
640+
PLLCSR &= ~(1<<PLLE); // stop PLL
641+
}
642+
643+
static inline void USB_ClockEnable()
644+
{
645+
UHWCON |= (1<<UVREGE); // power internal reg
646+
USBCON = (1<<USBE) | (1<<FRZCLK); // clock frozen, usb enabled
647+
648+
// ATmega32U4
649+
#if defined(PINDIV)
650+
#if F_CPU == 16000000UL
651+
PLLCSR |= (1<<PINDIV); // Need 16 MHz xtal
652+
#elif F_CPU == 8000000UL
653+
PLLCSR &= ~(1<<PINDIV); // Need 8 MHz xtal
654+
#else
655+
#error "Clock rate of F_CPU not supported"
656+
#endif
657+
658+
// AT90USB646, AT90USB647, AT90USB1286, AT90USB1287
659+
#elif defined(PLLP2)
660+
#if F_CPU == 16000000UL
661+
#if defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
662+
// For Atmel AT90USB128x only. Do not use with Atmel AT90USB64x.
663+
PLLCSR = (PLLCSR & ~(1<<PLLP1)) | ((1<<PLLP2) | (1<<PLLP0)); // Need 16 MHz xtal
664+
#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__)
665+
// For AT90USB64x only. Do not use with AT90USB128x.
666+
PLLCSR = (PLLCSR & ~(1<<PLLP0)) | ((1<<PLLP2) | (1<<PLLP1)); // Need 16 MHz xtal
667+
#else
668+
#error "USB Chip not supported, please defined method of USB PLL initialization"
669+
#endif
670+
#elif F_CPU == 8000000UL
671+
// for Atmel AT90USB128x and AT90USB64x
672+
PLLCSR = (PLLCSR & ~(1<<PLLP2)) | ((1<<PLLP1) | (1<<PLLP0)); // Need 8 MHz xtal
673+
#else
674+
#error "Clock rate of F_CPU not supported"
675+
#endif
676+
#else
677+
#error "USB Chip not supported, please defined method of USB PLL initialization"
678+
#endif
679+
680+
PLLCSR |= (1<<PLLE);
681+
while (!(PLLCSR & (1<<PLOCK))) // wait for lock pll
682+
{
683+
}
684+
685+
// Some tests on specific versions of macosx (10.7.3), reported some
686+
// strange behaviuors when the board is reset using the serial
687+
// port touch at 1200 bps. This delay fixes this behaviour.
688+
delay(1);
689+
USBCON = (USBCON & ~(1<<FRZCLK)) | (1<<OTGPADE); // start USB clock, enable VBUS Pad
690+
691+
#if defined(RSTCPU)
692+
UDCON &= ~((1<<RSTCPU) | (1<<LSM) | (1<<RMWKUP) | (1<<DETACH)); // enable attach resistor, set full speed mode
693+
#else
694+
// AT90USB64x and AT90USB128x don't have RSTCPU
695+
UDCON &= ~((1<<LSM) | (1<<RMWKUP) | (1<<DETACH)); // enable attach resistor, set full speed mode
696+
#endif
697+
}
698+
699+
612700
// General interrupt
613701
ISR(USB_GEN_vect)
614702
{
615703
u8 udint = UDINT;
616-
UDINT = 0;
704+
UDINT &= ~((1<<EORSTI) | (1<<SOFI)); // clear the IRQ flags for the IRQs which are handled here, except WAKEUPI and SUSPI (see below)
617705

618706
// End of Reset
619707
if (udint & (1<<EORSTI))
@@ -636,6 +724,30 @@ ISR(USB_GEN_vect)
636724
if (RxLEDPulse && !(--RxLEDPulse))
637725
RXLED0;
638726
}
727+
728+
// the WAKEUPI interrupt is triggered as soon as there are non-idle patterns on the data
729+
// lines. Thus, the WAKEUPI interrupt can occur even if the controller is not in the "suspend" mode.
730+
// Therefore the we enable it only when USB is suspended
731+
if (udint & (1<<WAKEUPI))
732+
{
733+
UDIEN = (UDIEN & ~(1<<WAKEUPE)) | (1<<SUSPE); // Disable interrupts for WAKEUP and enable interrupts for SUSPEND
734+
735+
//TODO
736+
// WAKEUPI shall be cleared by software (USB clock inputs must be enabled before).
737+
//USB_ClockEnable();
738+
UDINT &= ~(1<<WAKEUPI);
739+
_usbSuspendState = (_usbSuspendState & ~(1<<SUSPI)) | (1<<WAKEUPI);
740+
}
741+
else if (udint & (1<<SUSPI)) // only one of the WAKEUPI / SUSPI bits can be active at time
742+
{
743+
UDIEN = (UDIEN & ~(1<<SUSPE)) | (1<<WAKEUPE); // Disable interrupts for SUSPEND and enable interrupts for WAKEUP
744+
745+
//TODO
746+
//USB_ClockDisable();
747+
748+
UDINT &= ~((1<<WAKEUPI) | (1<<SUSPI)); // clear any already pending WAKEUP IRQs and the SUSPI request
749+
_usbSuspendState = (_usbSuspendState & ~(1<<WAKEUPI)) | (1<<SUSPI);
750+
}
639751
}
640752

641753
// VBUS or counting frames
@@ -659,24 +771,12 @@ USBDevice_::USBDevice_()
659771
void USBDevice_::attach()
660772
{
661773
_usbConfiguration = 0;
662-
UHWCON = 0x01; // power internal reg
663-
USBCON = (1<<USBE)|(1<<FRZCLK); // clock frozen, usb enabled
664-
#if F_CPU == 16000000UL
665-
PLLCSR = 0x12; // Need 16 MHz xtal
666-
#elif F_CPU == 8000000UL
667-
PLLCSR = 0x02; // Need 8 MHz xtal
668-
#endif
669-
while (!(PLLCSR & (1<<PLOCK))) // wait for lock pll
670-
;
774+
_usbCurrentStatus = 0;
775+
_usbSuspendState = 0;
776+
USB_ClockEnable();
671777

672-
// Some tests on specific versions of macosx (10.7.3), reported some
673-
// strange behaviuors when the board is reset using the serial
674-
// port touch at 1200 bps. This delay fixes this behaviour.
675-
delay(1);
676-
677-
USBCON = ((1<<USBE)|(1<<OTGPADE)); // start USB clock
678-
UDIEN = (1<<EORSTE)|(1<<SOFE); // Enable interrupts for EOR (End of Reset) and SOF (start of frame)
679-
UDCON = 0; // enable attach resistor
778+
UDINT &= ~((1<<WAKEUPI) | (1<<SUSPI)); // clear already pending WAKEUP / SUSPEND requests
779+
UDIEN = (1<<EORSTE) | (1<<SOFE) | (1<<SUSPE); // Enable interrupts for EOR (End of Reset), SOF (start of frame) and SUSPEND
680780

681781
TX_RX_LED_INIT;
682782
}
@@ -696,4 +796,25 @@ void USBDevice_::poll()
696796
{
697797
}
698798

799+
800+
bool USBDevice_::wakeupHost()
801+
{
802+
// clear any previous wakeup request which might have been set but could be processed at that time
803+
// e.g. because the host was not suspended at that time
804+
UDCON &= ~(1 << RMWKUP);
805+
806+
if(!(UDCON & (1 << RMWKUP))
807+
&& (_usbSuspendState & (1<<SUSPI))
808+
&& (_usbCurrentStatus & FEATURE_REMOTE_WAKEUP_ENABLED))
809+
{
810+
// This short version will only work, when the device has not been suspended. Currently the
811+
// Arduino core doesn't handle SUSPEND at all, so this is ok.
812+
USB_ClockEnable();
813+
UDCON |= (1 << RMWKUP); // send the wakeup request
814+
return true;
815+
}
816+
817+
return false;
818+
}
819+
699820
#endif /* if defined(USBCON) */

hardware/arduino/avr/cores/arduino/USBCore.h

+10-1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,15 @@
7979
#define USB_INTERFACE_DESCRIPTOR_TYPE 4
8080
#define USB_ENDPOINT_DESCRIPTOR_TYPE 5
8181

82+
// usb_20.pdf Table 9.6 Standard Feature Selectors
83+
#define DEVICE_REMOTE_WAKEUP 1
84+
#define ENDPOINT_HALT 2
85+
#define TEST_MODE 3
86+
87+
// usb_20.pdf Figure 9-4. Information Returned by a GetStatus() Request to a Device
88+
#define FEATURE_SELFPOWERED_ENABLED (1 << 0)
89+
#define FEATURE_REMOTE_WAKEUP_ENABLED (1 << 1)
90+
8291
#define USB_DEVICE_CLASS_COMMUNICATIONS 0x02
8392
#define USB_DEVICE_CLASS_HUMAN_INTERFACE 0x03
8493
#define USB_DEVICE_CLASS_STORAGE 0x08
@@ -282,7 +291,7 @@ typedef struct
282291
{ 18, 1, 0x200, _class,_subClass,_proto,_packetSize0,_vid,_pid,_version,_im,_ip,_is,_configs }
283292

284293
#define D_CONFIG(_totalLength,_interfaces) \
285-
{ 9, 2, _totalLength,_interfaces, 1, 0, USB_CONFIG_BUS_POWERED, USB_CONFIG_POWER_MA(500) }
294+
{ 9, 2, _totalLength,_interfaces, 1, 0, USB_CONFIG_BUS_POWERED | USB_CONFIG_REMOTE_WAKEUP, USB_CONFIG_POWER_MA(500) }
286295

287296
#define D_INTERFACE(_n,_numEndpoints,_class,_subClass,_protocol) \
288297
{ 9, 4, _n, 0, _numEndpoints, _class,_subClass, _protocol, 0 }

0 commit comments

Comments
 (0)