/* Copyright 2011 DJ Delorie This file 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, or (at your option) any later version. This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License (available at www.gnu.org) for more details. The purpose of this file is to provide hints as to how the R8C/3MK's USB core works, as it came from a working project. But it's not intended to be used as-is in another working project, as it requires many other supporting routines and files which aren't included. */ #include "bsp.h" #include "usb.h" /* These are the USB descriptors we'll be returning. The values and structures are standard USB and can be found in the USB spec (www.usb.org). */ static USB_device_descriptor my_device_d = { sizeof(USB_device_descriptor), DESCRIPTOR_DEVICE, 0x0110, 0, 0, 0, 64, /* max packet size for control pipe */ MY_VENDOR_ID, 0x444a, /* "DJ" */ 0x0101, /* device release number */ 1, 0, 0, 1 }; static USB_configuration_descriptor my_configuration_d = { sizeof(USB_configuration_descriptor), DESCRIPTOR_CONFIGURATION, sizeof(USB_configuration_descriptor), /* total length */ 1, /* num interfaces */ 1, /* configuration value */ 0, CONFIG_ATTR_SELF_POWERED, 10, /* 20 mA */ }; static USB_interface_descriptor my_interface_d = { sizeof(USB_interface_descriptor), DESCRIPTOR_INTERFACE, 0, 0, 2, /* num endpoints */ 0xff, /* class */ 0xff, /* subclass */ 0xff, /* protocol */ 0 }; static USB_endpoint_descriptor my_endpoint1in_d = { sizeof(USB_endpoint_descriptor), DESCRIPTOR_ENDPOINT, 1 | EP_IN, EP_BULK, 64, 1 }; static USB_endpoint_descriptor my_endpoint2out_d = { sizeof(USB_endpoint_descriptor), DESCRIPTOR_ENDPOINT, 2 | EP_OUT, EP_BULK, 64, 1 }; static USB_lang_descriptor my_lang_d = { 4, DESCRIPTOR_STRING, 0x0409, }; /* Note that USB strings are 16-bit Unicode, and not all compilers can do 16-bit strings (Linux, for example, uses 32-bit wchar_t). */ static USB_string_descriptor my_string1_d = { 4 + 2*10, DESCRIPTOR_STRING, { 'D', 'J', ' ', 'D', 'e', 'l', 'o', 'r', 'i', 'e' } }; /* My system registers interrupt handlers by name. If you have some other method, you need to arrange for this routine to be called whtn the USBINT interrupt happens. */ void __attribute__((interrupt)) usbint_handler () { uint16_t buf[4]; int first; while (1) { /* We only check for control transfers here, but it's structured to allow you to test other bits within the while() loop. Only when no bits are detected as set does the loop exit, so we end up servicing all pending interrupts. */ if (intsts0_ctrt) { intsts0_ctrt = 0; /* These are stored only so we can print them at the end of the handler, for debugging. The R8C automatically decodes any SETUP packets sent to the control pipe, and fills in the parameters here. You just have to check their values and send the right reply. */ buf[0] = usbreq; buf[1] = usbval; buf[2] = usbindx; buf[3] = usbleng; switch (intsts0_ctsq) { case 0: /* idle */ break; case 1: /* control read data */ switch (usbreq) { case 0x0680: /* request descriptor */ switch (usbval) { case 0x0100: /* device */ usb_write_pipe (0, (uint8_t *)&my_device_d, sizeof(my_device_d)); break; case 0x0200: /* configuration */ /* This is called twice if needed. if there isn't enough room, it's called again with a bigger buffer. The device must send all interface and endpoint descriptors along with the configuration descriptor. */ my_configuration_d.wTotalLength = sizeof (my_configuration_d) + sizeof(my_interface_d) + sizeof(my_endpoint1in_d) + sizeof(my_endpoint2out_d); first = (usbleng < my_configuration_d.wTotalLength) ? 1 : 0; usb_write_start (0); usb_write_data ((uint8_t *)&my_configuration_d, sizeof(my_configuration_d)); if (!first) { usb_write_data ((uint8_t *)&my_interface_d, sizeof(my_interface_d)); usb_write_data ((uint8_t *)&my_endpoint1in_d, sizeof(my_endpoint1in_d)); usb_write_data ((uint8_t *)&my_endpoint2out_d, sizeof(my_endpoint2out_d)); } usb_write_done (); break; /* There's one of these for each string, the LSB is the index. */ case 0x0300: usb_write_pipe (0, &my_lang_d, my_lang_d.bLength); break; case 0x0301: usb_write_pipe (0, &my_string1_d, my_string1_d.bLength); break; } break; default: usb_write_pipe (0, "12345678", 8); break; } break; case 5: /* control write no-data-status */ switch (usbreq) { case 0x0900: /* set config */ /* We only have one config, so we just ACK the request. */ usb_write_pipe (0, 0, 0); break; } case 2: /* control read status */ case 3: /* control write data */ case 4: /* control write status */ case 6: /* sequence error */ case 7: /* doesn't happen */ goto done; } } else break; } done: cprintf(" %d a %d r %w v %w i %w l %w\n", intsts0_ctsq, usbaddr, buf[0], buf[1], buf[2], buf[3]); return; } void usb_core_init () { #if 0 /* If the R8C isn't already running on the PLL, set it up here. */ prcr_prc0 = 1; /* enable writing to cm0/1 */ cm1_3 = 1; /* enable xin/xout pins */ cm0_5 = 1; /* start 12MHz input clock */ while (ocd_ocd3) /* wait for it to oscillate */ asm(""); plc0 = 0x20; /* divide-by-4 (3MHz) */ plc1 = 0x10; /* multiply by 32 (96 MHz) */ pldiv = 0x08; /* divide by 2 (48 MHz) */ plc0_0 = 1; /* power on */ plc0_1 = 1; plc0_6 = 1; plc0_7 = 1; wait_ms (2); #endif /* If you forget to set this, the USB module will mostly work if you pull D+ high manually - yes, DP and DM will work even when disabled, but DPUPE and VBUS will *not*. This register is NOT documented in the USB chapter, but in the I/O Ports chapter. */ usbsr0 = 0x3c; /* enable DPUPE, DP, DM, VBUS. Disable OVRCURA, VBUSEN. */ usbmc_pxxcon = 1; /* enable vddusbe bit */ usbmc_vddusbe = 1; /* use internal 3.3v supply (Vdd>4.0v) */ syscfg = 0; syscfg_scke = 1; /* enable clock */ syscfg_usbe = 1; /* enable USB */ syscfg_dcfm = 0; /* select Function mode */ syscfg_dprpu = 1; /* enable D+ pull-up (connect) */ dcpmaxp = 0x40; /* 64 byte buffers for control pipe */ intsts0 = 0; intsts1 = 0; intenb0 = 0; intenb0_ctre = 1; usbintic = 5; } void usb_write_start (int pipe) { intsts0_valid = 0; dcpctr_pid = 1; cfifosel = pipe | 0x4020; } void usb_write_data (void *vdata, int len) { uint8_t *data = (uint8_t *)vdata; int i; p8 = 0xff; while (dcpctr_bsts == 0) ; p8 = 0x0; for (i=0; i