PageRenderTime 59ms CodeModel.GetById 23ms app.highlight 31ms RepoModel.GetById 0ms app.codeStats 1ms

/cpu/cc2538/usb/usb-arch.c

https://bitbucket.org/homadeus/contiki
C | 1256 lines | 931 code | 173 blank | 152 comment | 165 complexity | 912e0237f48163ecb3ce4628f7d9aaae MD5 | raw file
   1/*
   2 * Copyright (c) 2012, Philippe Retornaz
   3 * Copyright (c) 2012, EPFL STI IMT LSRO1 -- Mobots group
   4 *
   5 * Redistribution and use in source and binary forms, with or without
   6 * modification, are permitted provided that the following conditions
   7 * are met:
   8 * 1. Redistributions of source code must retain the above copyright
   9 *    notice, this list of conditions and the following disclaimer.
  10 * 2. Redistributions in binary form must reproduce the above copyright
  11 *    notice, this list of conditions and the following disclaimer in the
  12 *    documentation and/or other materials provided with the distribution.
  13 *
  14 * 3. Neither the name of the copyright holder nor the names of its
  15 *    contributors may be used to endorse or promote products derived
  16 *    from this software without specific prior written permission.
  17 *
  18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
  22 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  29 * OF THE POSSIBILITY OF SUCH DAMAGE.
  30 */
  31/**
  32 * \addtogroup cc2538-usb
  33 * @{
  34 *
  35 * \file
  36 *     Arch-specific routines for the cc2538 USB controller. Heavily based on
  37 *     the cc2530 driver written by Philippe Retornaz
  38 */
  39#include "contiki.h"
  40#include "energest.h"
  41#include "usb-arch.h"
  42#include "usb-api.h"
  43#include "dev/usb-regs.h"
  44#include "dev/nvic.h"
  45#include "dev/gpio.h"
  46#include "dev/ioc.h"
  47#include "dev/udma.h"
  48#include "sys/clock.h"
  49#include "reg.h"
  50
  51#include "dev/watchdog.h"
  52
  53#include <stdint.h>
  54/*---------------------------------------------------------------------------*/
  55/* EP max FIFO sizes without double buffering */
  56#if CTRL_EP_SIZE > 32
  57#error Control endpoint size too big
  58#endif
  59
  60#if USB_EP1_SIZE > 32
  61#error Endpoint 1 size too big
  62#endif
  63
  64#if USB_EP2_SIZE > 64
  65#error Endpoint 2 size too big
  66#endif
  67
  68#if USB_EP3_SIZE > 128
  69#error Endpoint 3 size too big
  70#endif
  71
  72#if USB_EP4_SIZE > 256
  73#error Endpoint 4 size too big
  74#endif
  75
  76#if USB_EP5_SIZE > 512
  77#error Endpoint 5 size too big
  78#endif
  79/*---------------------------------------------------------------------------*/
  80/* uDMA transfer threshold. Use DMA only for data size higher than this */
  81#define UDMA_SIZE_THRESHOLD 8
  82
  83/* uDMA channel control persistent flags */
  84#define UDMA_TX_FLAGS (UDMA_CHCTL_ARBSIZE_128 | UDMA_CHCTL_XFERMODE_AUTO \
  85    | UDMA_CHCTL_SRCSIZE_8 | UDMA_CHCTL_DSTSIZE_8 \
  86    | UDMA_CHCTL_SRCINC_8 | UDMA_CHCTL_DSTINC_NONE)
  87
  88#define UDMA_RX_FLAGS (UDMA_CHCTL_ARBSIZE_128 | UDMA_CHCTL_XFERMODE_AUTO \
  89    | UDMA_CHCTL_SRCSIZE_8 | UDMA_CHCTL_DSTSIZE_8 \
  90    | UDMA_CHCTL_SRCINC_NONE | UDMA_CHCTL_DSTINC_8)
  91/*---------------------------------------------------------------------------*/
  92static const uint16_t ep_xfer_size[] = {
  93  CTRL_EP_SIZE,
  94  USB_EP1_SIZE,
  95  USB_EP2_SIZE,
  96  USB_EP3_SIZE,
  97  USB_EP4_SIZE,
  98  USB_EP5_SIZE,
  99};
 100/*---------------------------------------------------------------------------*/
 101typedef struct _USBBuffer usb_buffer;
 102/*---------------------------------------------------------------------------*/
 103struct usb_endpoint {
 104  uint8_t halted;
 105  uint8_t addr;
 106  uint8_t flags;
 107  usb_buffer *buffer;
 108  struct process *event_process;
 109  unsigned int events;
 110  uint16_t xfer_size;
 111};
 112typedef struct usb_endpoint usb_endpoint_t;
 113/*---------------------------------------------------------------------------*/
 114#define EP_STATUS_IDLE                 0
 115#define EP_STATUS_RX                   1
 116#define EP_STATUS_TX                   2
 117
 118#define USB_EP_FLAGS_TYPE_MASK      0x03
 119#define USB_EP_FLAGS_TYPE_BULK      0x00
 120#define USB_EP_FLAGS_TYPE_CONTROL   0x01
 121#define USB_EP_FLAGS_TYPE_ISO       0x02
 122#define USB_EP_FLAGS_TYPE_INTERRUPT 0x03
 123#define USB_EP_FLAGS_ENABLED 	      0x04
 124
 125#define EP_TYPE(ep)          ((ep)->flags & USB_EP_FLAGS_TYPE_MASK)
 126#define IS_EP_TYPE(ep, type) (EP_TYPE(ep) == (type))
 127#define IS_CONTROL_EP(ep)    IS_EP_TYPE(ep, USB_EP_FLAGS_TYPE_CONTROL)
 128#define IS_BULK_EP(ep)       IS_EP_TYPE(ep, USB_EP_FLAGS_TYPE_BULK)
 129#define IS_INTERRUPT_EP(ep)  IS_EP_TYPE(ep, USB_EP_FLAGS_TYPE_INTERRUPT)
 130#define IS_ISO_EP(ep)        IS_EP_TYPE(ep, USB_EP_FLAGS_TYPE_ISO)
 131
 132#define USBIIE_INEPxIE(x)    (1 << x)
 133#define USBOIE_OUEPxIE(x)    (1 << x)
 134#define EPxIF(x)             (1 << x)
 135
 136#define USB_READ_BLOCK              0x01
 137#define USB_WRITE_NOTIFY            0x02
 138#define USB_READ_NOTIFY             0x02
 139#define USB_READ_FAIL	              0x04
 140
 141/* Index in endpoint array */
 142#define EP_INDEX(addr) ((addr) & 0x7f)
 143
 144/* Get address of endpoint struct */
 145#define EP_STRUCT(addr) &usb_endpoints[EP_INDEX(addr)];
 146
 147/* Number of hardware endpoint */
 148#define EP_HW_NUM(addr) ((addr) & 0x7f)
 149/*---------------------------------------------------------------------------*/
 150static usb_endpoint_t usb_endpoints[USB_MAX_ENDPOINTS];
 151struct process *event_process = 0;
 152volatile static unsigned int events = 0;
 153static uint8_t ep0status;
 154/*---------------------------------------------------------------------------*/
 155static uint8_t ep0_tx(void);
 156static uint8_t ep_tx(uint8_t ep_hw);
 157static void in_ep_interrupt_handler(uint8_t ep_hw);
 158static void out_ep_interrupt_handler(uint8_t ep_hw);
 159static void ep0_interrupt_handler(void);
 160/*---------------------------------------------------------------------------*/
 161static void
 162notify_process(unsigned int e)
 163{
 164  events |= e;
 165  if(event_process) {
 166    process_poll(event_process);
 167  }
 168}
 169/*---------------------------------------------------------------------------*/
 170static void
 171notify_ep_process(usb_endpoint_t *ep, unsigned int e)
 172{
 173  ep->events |= e;
 174  if(ep->event_process) {
 175    process_poll(ep->event_process);
 176  }
 177}
 178/*---------------------------------------------------------------------------*/
 179void
 180usb_set_ep_event_process(unsigned char addr, struct process *p)
 181{
 182  usb_endpoint_t *ep = EP_STRUCT(addr);
 183
 184  ep->event_process = p;
 185}
 186/*---------------------------------------------------------------------------*/
 187void
 188usb_arch_set_global_event_process(struct process *p)
 189{
 190  event_process = p;
 191}
 192/*---------------------------------------------------------------------------*/
 193unsigned int
 194usb_arch_get_global_events(void)
 195{
 196  uint8_t flag;
 197  volatile unsigned int e;
 198
 199  flag = nvic_interrupt_en_save(NVIC_INT_USB);
 200
 201  e = events;
 202  events = 0;
 203
 204  nvic_interrupt_en_restore(NVIC_INT_USB, flag);
 205
 206  return e;
 207}
 208/*---------------------------------------------------------------------------*/
 209unsigned int
 210usb_get_ep_events(uint8_t addr)
 211{
 212  volatile unsigned int e;
 213  uint8_t flag;
 214  usb_endpoint_t *ep = EP_STRUCT(addr);
 215
 216  flag = nvic_interrupt_en_save(NVIC_INT_USB);
 217
 218  e = ep->events;
 219  ep->events = 0;
 220
 221  nvic_interrupt_en_restore(NVIC_INT_USB, flag);
 222
 223  return e;
 224}
 225/*---------------------------------------------------------------------------*/
 226static void
 227read_hw_buffer(uint8_t *to, uint8_t hw_ep, unsigned int len)
 228{
 229  uint32_t fifo_addr = USB_F0 + (hw_ep << 3);
 230
 231  if(USB_ARCH_CONF_DMA && len > UDMA_SIZE_THRESHOLD) {
 232    /* Set the transfer source and destination addresses */
 233    udma_set_channel_src(USB_ARCH_CONF_RX_DMA_CHAN, fifo_addr);
 234    udma_set_channel_dst(USB_ARCH_CONF_RX_DMA_CHAN,
 235                         (uint32_t)(to) + len - 1);
 236
 237    /* Configure the control word */
 238    udma_set_channel_control_word(USB_ARCH_CONF_RX_DMA_CHAN,
 239                                  UDMA_RX_FLAGS | udma_xfer_size(len));
 240
 241    /* Enabled the RF RX uDMA channel */
 242    udma_channel_enable(USB_ARCH_CONF_RX_DMA_CHAN);
 243
 244    /* Trigger the uDMA transfer */
 245    udma_channel_sw_request(USB_ARCH_CONF_RX_DMA_CHAN);
 246
 247    /* Wait for the transfer to complete. */
 248    while(udma_channel_get_mode(USB_ARCH_CONF_RX_DMA_CHAN));
 249  } else {
 250    while(len--) {
 251      *to++ = REG(fifo_addr);
 252    }
 253  }
 254}
 255/*---------------------------------------------------------------------------*/
 256static void
 257write_hw_buffer(uint8_t hw_ep, uint8_t *from, unsigned int len)
 258{
 259  uint32_t fifo_addr = USB_F0 + (hw_ep << 3);
 260
 261  if(USB_ARCH_CONF_DMA && len > UDMA_SIZE_THRESHOLD) {
 262    /* Set the transfer source and destination addresses */
 263    udma_set_channel_src(USB_ARCH_CONF_TX_DMA_CHAN,
 264                         (uint32_t)(from) + len - 1);
 265    udma_set_channel_dst(USB_ARCH_CONF_TX_DMA_CHAN, fifo_addr);
 266
 267    /* Configure the control word */
 268    udma_set_channel_control_word(USB_ARCH_CONF_TX_DMA_CHAN,
 269                                  UDMA_TX_FLAGS | udma_xfer_size(len));
 270
 271    /* Enabled the RF RX uDMA channel */
 272    udma_channel_enable(USB_ARCH_CONF_TX_DMA_CHAN);
 273
 274    /* Trigger the uDMA transfer */
 275    udma_channel_sw_request(USB_ARCH_CONF_TX_DMA_CHAN);
 276
 277    /* Wait for the transfer to complete. */
 278    while(udma_channel_get_mode(USB_ARCH_CONF_TX_DMA_CHAN));
 279  } else {
 280    while(len--) {
 281      REG(fifo_addr) = *from++;
 282    }
 283  }
 284}
 285/*---------------------------------------------------------------------------*/
 286static void
 287reset(void)
 288{
 289  uint8_t e;
 290
 291  for(e = 0; e < USB_MAX_ENDPOINTS; e++) {
 292    if(usb_endpoints[e].flags & USB_EP_FLAGS_ENABLED) {
 293      usb_buffer *buffer = usb_endpoints[e].buffer;
 294
 295      usb_endpoints[e].flags = 0;
 296      usb_disable_endpoint(e);
 297      while(buffer) {
 298        buffer->flags &= ~USB_BUFFER_SUBMITTED;
 299        buffer = buffer->next;
 300      }
 301    }
 302  }
 303  usb_arch_setup_control_endpoint(0);
 304}
 305/*---------------------------------------------------------------------------*/
 306/* Init USB */
 307void
 308usb_arch_setup(void)
 309{
 310  uint8_t i;
 311
 312  /* Switch on USB PLL & USB module */
 313  REG(USB_CTRL) = USB_CTRL_USB_EN | USB_CTRL_PLL_EN;
 314
 315  /* Wait until USB PLL is stable */
 316  while(!(REG(USB_CTRL) & USB_CTRL_PLL_LOCKED));
 317
 318  /* Enable pull-up on usb port */
 319  GPIO_SET_OUTPUT(USB_PULLUP_PORT, USB_PULLUP_PIN_MASK);
 320  REG((USB_PULLUP_PORT | GPIO_DATA) + (USB_PULLUP_PIN_MASK << 2)) = 1;
 321
 322  for(i = 0; i < USB_MAX_ENDPOINTS; i++) {
 323    usb_endpoints[i].flags = 0;
 324    usb_endpoints[i].event_process = 0;
 325  }
 326
 327  reset();
 328
 329  /* Disable all EP interrupts, EP0 interrupt will be enabled later */
 330  REG(USB_IIE) = 0;
 331  REG(USB_OIE) = 0;
 332
 333  /* Initialise the USB control structures */
 334  if(USB_ARCH_CONF_DMA) {
 335    /* Disable peripheral triggers for our channels */
 336    udma_channel_mask_set(USB_ARCH_CONF_RX_DMA_CHAN);
 337    udma_channel_mask_set(USB_ARCH_CONF_TX_DMA_CHAN);
 338  }
 339
 340  nvic_interrupt_enable(NVIC_INT_USB);
 341}
 342/*---------------------------------------------------------------------------*/
 343void
 344usb_submit_recv_buffer(uint8_t addr, usb_buffer *buffer)
 345{
 346  usb_buffer **tailp;
 347  uint8_t flag;
 348  usb_endpoint_t *ep = EP_STRUCT(addr);
 349
 350  if(!(ep->flags & USB_EP_FLAGS_ENABLED)) {
 351    return;
 352  }
 353
 354  if(buffer->data == NULL && EP_HW_NUM(addr) == 0) {
 355    if(buffer->flags & USB_BUFFER_NOTIFY) {
 356      notify_ep_process(ep, USB_EP_EVENT_NOTIFICATION);
 357    }
 358    return;
 359  }
 360
 361  flag = nvic_interrupt_en_save(NVIC_INT_USB);
 362
 363  tailp = &ep->buffer;
 364  while(*tailp) {
 365    tailp = &(*tailp)->next;
 366  }
 367  *tailp = buffer;
 368  while(buffer) {
 369    buffer->flags |= USB_BUFFER_SUBMITTED;
 370    buffer = buffer->next;
 371  }
 372
 373  REG(USB_INDEX) = EP_HW_NUM(addr);
 374  if(!EP_HW_NUM(ep->addr)) {
 375    if(REG(USB_CS0) & USB_CS0_OUTPKT_RDY) {
 376      ep0_interrupt_handler();
 377    }
 378  } else {
 379    if(REG(USB_CSOL) & USB_CSOL_OUTPKT_RDY) {
 380      out_ep_interrupt_handler(EP_HW_NUM(ep->addr));
 381    }
 382  }
 383
 384  nvic_interrupt_en_restore(NVIC_INT_USB, flag);
 385}
 386/*---------------------------------------------------------------------------*/
 387void
 388usb_submit_xmit_buffer(uint8_t addr, usb_buffer *buffer)
 389{
 390  usb_buffer **tailp;
 391  uint8_t flag;
 392  uint8_t res;
 393  usb_endpoint_t *ep = EP_STRUCT(addr);
 394
 395  if(!(ep->flags & USB_EP_FLAGS_ENABLED)) {
 396    return;
 397  }
 398
 399  flag = nvic_interrupt_en_save(NVIC_INT_USB);
 400
 401  if(EP_HW_NUM(addr) == 0) {
 402    if(buffer->data == NULL) {
 403      /* We are asked to send a STATUS packet.
 404       * But the USB hardware will do this automatically
 405       * as soon as we release the HW FIFO. */
 406      REG(USB_INDEX) = 0;
 407      REG(USB_CS0) = USB_CS0_CLR_OUTPKT_RDY | USB_CS0_DATA_END;
 408      notify_ep_process(ep, USB_EP_EVENT_NOTIFICATION);
 409      nvic_interrupt_en_restore(NVIC_INT_USB, flag);
 410      return;
 411    } else {
 412      /* Release the HW FIFO */
 413      REG(USB_INDEX) = 0;
 414      REG(USB_CS0) = USB_CS0_CLR_OUTPKT_RDY;
 415    }
 416  }
 417
 418  tailp = &ep->buffer;
 419  while(*tailp) {
 420    tailp = &(*tailp)->next;
 421  }
 422  *tailp = buffer;
 423  while(buffer) {
 424    buffer->flags |= USB_BUFFER_SUBMITTED | USB_BUFFER_IN;
 425    buffer = buffer->next;
 426  }
 427
 428  REG(USB_INDEX) = EP_HW_NUM(ep->addr);
 429  if(EP_HW_NUM(ep->addr)) {
 430    res = ep_tx(EP_HW_NUM(ep->addr));
 431  } else {
 432    res = ep0_tx();
 433  }
 434
 435  nvic_interrupt_en_restore(NVIC_INT_USB, flag);
 436
 437  if(res & USB_WRITE_NOTIFY) {
 438    notify_ep_process(ep, USB_EP_EVENT_NOTIFICATION);
 439  }
 440}
 441/*---------------------------------------------------------------------------*/
 442static void
 443ep0_setup(void)
 444{
 445  REG(USB_IIE) |= USB_IIE_EP0IE;
 446}
 447/*---------------------------------------------------------------------------*/
 448static void
 449in_ep_setup(uint8_t addr)
 450{
 451  uint8_t ei = EP_HW_NUM(addr);
 452  usb_endpoint_t *ep = EP_STRUCT(addr);
 453
 454  /* Enable IN EP interrupt */
 455  REG(USB_IIE) |= USBIIE_INEPxIE(ei);
 456
 457  /* Set internal FIFO size */
 458  REG(USB_MAXI) = ep->xfer_size / 8;
 459
 460  if(IS_ISO_EP(ep)) {
 461    REG(USB_CSIH) |= USB_CSOH_ISO;
 462  } else {
 463    REG(USB_CSIH) &= ~USB_CSOH_ISO;
 464  }
 465}
 466/*---------------------------------------------------------------------------*/
 467static void
 468out_ep_setup(uint8_t addr)
 469{
 470  uint8_t ei = EP_HW_NUM(addr);
 471  usb_endpoint_t *ep = EP_STRUCT(addr);
 472
 473  /* Enable OUT EP interrupt */
 474  REG(USB_OIE) |= USBOIE_OUEPxIE(ei);
 475
 476  /* Set internal FIFO size */
 477  REG(USB_MAXO) = ep->xfer_size / 8;
 478
 479  if(IS_ISO_EP(ep)) {
 480    REG(USB_CSOH) |= USB_CSOH_ISO;
 481  } else {
 482    REG(USB_CSOH) &= ~USB_CSOH_ISO;
 483  }
 484}
 485/*---------------------------------------------------------------------------*/
 486static void
 487ep_setup(uint8_t addr)
 488{
 489  uint8_t ei = EP_HW_NUM(addr);
 490  uint8_t flag;
 491  usb_endpoint_t *ep = EP_STRUCT(addr);
 492
 493  ep->halted = 0;
 494  ep->flags |= USB_EP_FLAGS_ENABLED;
 495  ep->buffer = 0;
 496  ep->addr = addr;
 497  ep->events = 0;
 498  ep->xfer_size = ep_xfer_size[ei];
 499
 500  flag = nvic_interrupt_en_save(NVIC_INT_USB);
 501
 502  /* Select endpoint register */
 503  REG(USB_INDEX) = ei;
 504
 505  /* EP0 requires special handing */
 506  if(ei == 0) {
 507    ep0_setup();
 508  } else {
 509    if(addr & 0x80) {
 510      in_ep_setup(addr);
 511    } else {
 512      out_ep_setup(addr);
 513    }
 514  }
 515
 516  nvic_interrupt_en_restore(NVIC_INT_USB, flag);
 517}
 518/*---------------------------------------------------------------------------*/
 519void
 520usb_arch_setup_iso_endpoint(uint8_t addr)
 521{
 522  usb_endpoint_t *ep = EP_STRUCT(addr);
 523
 524  ep->flags = USB_EP_FLAGS_TYPE_ISO;
 525
 526  ep_setup(addr);
 527}
 528/*---------------------------------------------------------------------------*/
 529void
 530usb_arch_setup_control_endpoint(uint8_t addr)
 531{
 532  usb_endpoint_t *ep = EP_STRUCT(addr);
 533
 534  ep->flags = USB_EP_FLAGS_TYPE_CONTROL;
 535
 536  ep_setup(addr);
 537}
 538/*---------------------------------------------------------------------------*/
 539void
 540usb_arch_setup_bulk_endpoint(uint8_t addr)
 541{
 542  usb_endpoint_t *ep = EP_STRUCT(addr);
 543
 544  ep->flags = USB_EP_FLAGS_TYPE_BULK;
 545
 546  ep_setup(addr);
 547}
 548/*---------------------------------------------------------------------------*/
 549void
 550usb_arch_setup_interrupt_endpoint(uint8_t addr)
 551{
 552  usb_endpoint_t *ep = EP_STRUCT(addr);
 553
 554  ep->flags = USB_EP_FLAGS_TYPE_INTERRUPT;
 555
 556  ep_setup(addr);
 557}
 558/*---------------------------------------------------------------------------*/
 559static void
 560ep0_dis(void)
 561{
 562  REG(USB_IIE) &= ~USB_IIE_EP0IE;
 563  /* Clear any pending status flags */
 564  REG(USB_CS0) = 0xC0;
 565}
 566/*---------------------------------------------------------------------------*/
 567static void
 568in_ep_dis(uint8_t addr)
 569{
 570  REG(USB_MAXI) = 0;
 571  REG(USB_IIE) &= ~USBIIE_INEPxIE(EP_HW_NUM(addr));
 572
 573  /* Flush pending */
 574  REG(USB_CSIL) = USB_CSIL_FLUSH_PACKET;
 575}
 576/*---------------------------------------------------------------------------*/
 577static void
 578out_ep_dis(uint8_t addr)
 579{
 580  REG(USB_MAXO) = 0;
 581  REG(USB_OIE) &= ~USBOIE_OUEPxIE(EP_HW_NUM(addr));
 582
 583  /* Flush pending */
 584  REG(USB_CSOL) = USB_CSIL_FLUSH_PACKET;
 585}
 586/*---------------------------------------------------------------------------*/
 587void
 588usb_arch_disable_endpoint(uint8_t addr)
 589{
 590  uint8_t ei = EP_HW_NUM(addr);
 591  uint8_t flag;
 592  usb_endpoint_t *ep = EP_STRUCT(addr);
 593
 594  ep->flags &= ~USB_EP_FLAGS_ENABLED;
 595
 596  flag = nvic_interrupt_en_save(NVIC_INT_USB);
 597
 598  REG(USB_INDEX) = ei;
 599  if(ei == 0) {
 600    ep0_dis();
 601  } else {
 602    if(addr & 0x80) {
 603      in_ep_dis(addr);
 604    } else {
 605      out_ep_dis(addr);
 606    }
 607  }
 608  nvic_interrupt_en_restore(NVIC_INT_USB, flag);
 609}
 610/*---------------------------------------------------------------------------*/
 611void
 612usb_arch_discard_all_buffers(uint8_t addr)
 613{
 614  usb_buffer *buffer;
 615  uint8_t flag;
 616  volatile usb_endpoint_t *ep = EP_STRUCT(addr);
 617
 618  flag = nvic_interrupt_en_save(NVIC_INT_USB);
 619
 620  buffer = ep->buffer;
 621  ep->buffer = NULL;
 622  nvic_interrupt_en_restore(NVIC_INT_USB, flag);
 623
 624  while(buffer) {
 625    buffer->flags &= ~USB_BUFFER_SUBMITTED;
 626    buffer = buffer->next;
 627  }
 628}
 629/*---------------------------------------------------------------------------*/
 630static void
 631set_stall(uint8_t addr, uint8_t stall)
 632{
 633  uint8_t ei = EP_HW_NUM(addr);
 634
 635  REG(USB_INDEX) = ei;
 636  if(ei == 0) {
 637    /* Stall is automatically deasserted on EP0 */
 638    if(stall) {
 639      ep0status = EP_STATUS_IDLE;
 640      REG(USB_CS0) |= USB_CS0_SEND_STALL | USB_CS0_OUTPKT_RDY;
 641    }
 642  } else {
 643    if(addr & 0x80) {
 644      if(stall) {
 645        REG(USB_CSIL) |= USB_CSIL_SEND_STALL;
 646      } else {
 647        REG(USB_CSIL) &= ~USB_CSIL_SEND_STALL;
 648      }
 649    } else {
 650      if(stall) {
 651        REG(USB_CSOL) |= USB_CSOL_SEND_STALL;
 652      } else {
 653        REG(USB_CSOL) &= ~USB_CSOL_SEND_STALL;
 654      }
 655    }
 656  }
 657}
 658/*---------------------------------------------------------------------------*/
 659void
 660usb_arch_control_stall(uint8_t addr)
 661{
 662  uint8_t ei = EP_HW_NUM(addr);
 663  uint8_t flag;
 664
 665  if(ei > USB_MAX_ENDPOINTS) {
 666    return;
 667  }
 668
 669  flag = nvic_interrupt_en_save(NVIC_INT_USB);
 670
 671  set_stall(addr, 1);
 672
 673  nvic_interrupt_en_restore(NVIC_INT_USB, flag);
 674}
 675/*---------------------------------------------------------------------------*/
 676void
 677usb_arch_halt_endpoint(uint8_t addr, int halt)
 678{
 679  uint8_t ei = EP_HW_NUM(addr);
 680  uint8_t flag;
 681  usb_endpoint_t *ep = EP_STRUCT(addr);
 682
 683  if(ei > USB_MAX_ENDPOINTS) {
 684    return;
 685  }
 686
 687  if(!(ep->flags & USB_EP_FLAGS_ENABLED)) {
 688    return;
 689  }
 690
 691  flag = nvic_interrupt_en_save(NVIC_INT_USB);
 692
 693  if(halt) {
 694    ep->halted = 0x1;
 695    set_stall(addr, 1);
 696  } else {
 697    ep->halted = 0;
 698    set_stall(addr, 0);
 699
 700    if(ep->buffer && (ep->buffer->flags & USB_BUFFER_HALT)) {
 701      ep->buffer->flags &= ~USB_BUFFER_SUBMITTED;
 702      if(ep->buffer->flags & USB_BUFFER_NOTIFY) {
 703        notify_ep_process(ep, USB_EP_EVENT_NOTIFICATION);
 704      }
 705      ep->buffer = ep->buffer->next;
 706    }
 707    if(ei) {
 708      out_ep_interrupt_handler(EP_HW_NUM(addr));
 709    }
 710  }
 711
 712  nvic_interrupt_en_restore(NVIC_INT_USB, flag);
 713}
 714/*---------------------------------------------------------------------------*/
 715void
 716usb_arch_set_configuration(uint8_t usb_configuration_value)
 717{
 718  return;
 719}
 720/*---------------------------------------------------------------------------*/
 721uint16_t
 722usb_arch_get_ep_status(uint8_t addr)
 723{
 724  uint8_t ei = EP_INDEX(addr);
 725  usb_endpoint_t *ep = EP_STRUCT(addr);
 726
 727  if(ei > USB_MAX_ENDPOINTS) {
 728    return 0;
 729  }
 730
 731  return ep->halted;
 732}
 733/*---------------------------------------------------------------------------*/
 734void
 735usb_arch_set_address(uint8_t addr)
 736{
 737  REG(USB_ADDR) = addr;
 738}
 739/*---------------------------------------------------------------------------*/
 740int
 741usb_arch_send_pending(uint8_t addr)
 742{
 743  uint8_t flag;
 744  uint8_t ret;
 745  uint8_t ei = EP_INDEX(addr);
 746
 747  flag = nvic_interrupt_en_save(NVIC_INT_USB);
 748
 749  REG(USB_INDEX) = ei;
 750  if(ei == 0) {
 751    ret = REG(USB_CS0) & USB_CS0_INPKT_RDY;
 752  } else {
 753    ret = REG(USB_CSIL) & USB_CSIL_INPKT_RDY;
 754  }
 755
 756  nvic_interrupt_en_restore(NVIC_INT_USB, flag);
 757
 758  return ret;
 759}
 760/*---------------------------------------------------------------------------*/
 761static unsigned int
 762get_receive_capacity(usb_buffer *buffer)
 763{
 764  unsigned int capacity = 0;
 765
 766  while(buffer &&
 767        !(buffer->flags & (USB_BUFFER_IN | USB_BUFFER_SETUP | USB_BUFFER_HALT))) {
 768    capacity += buffer->left;
 769    buffer = buffer->next;
 770  }
 771  return capacity;
 772}
 773/*---------------------------------------------------------------------------*/
 774static usb_buffer *
 775skip_buffers_until(usb_buffer *buffer, unsigned int mask, unsigned int flags,
 776                   uint8_t *resp)
 777{
 778  while(buffer && !((buffer->flags & mask) == flags)) {
 779    buffer->flags &= ~USB_BUFFER_SUBMITTED;
 780    buffer->flags |= USB_BUFFER_FAILED;
 781    if(buffer->flags & USB_BUFFER_NOTIFY) {
 782      *resp |= USB_READ_NOTIFY;
 783    }
 784    buffer = buffer->next;
 785  }
 786  return buffer;
 787}
 788/*---------------------------------------------------------------------------*/
 789static uint8_t
 790fill_buffers(usb_buffer *buffer, uint8_t hw_ep, unsigned int len,
 791             uint8_t short_packet)
 792{
 793  unsigned int t;
 794  uint8_t res = 0;
 795
 796  do {
 797    if(buffer->left < len) {
 798      t = buffer->left;
 799    } else {
 800      t = len;
 801    }
 802    len -= t;
 803    buffer->left -= t;
 804
 805    read_hw_buffer(buffer->data, hw_ep, t);
 806
 807    buffer->data += t;
 808
 809    if(len == 0) {
 810      break;
 811    }
 812
 813    buffer->flags &= ~(USB_BUFFER_SUBMITTED | USB_BUFFER_SHORT_PACKET);
 814    if(buffer->flags & USB_BUFFER_NOTIFY) {
 815      res |= USB_READ_NOTIFY;
 816    }
 817    buffer = buffer->next;
 818  } while(1);
 819
 820  if(short_packet) {
 821    buffer->flags |= USB_BUFFER_SHORT_PACKET;
 822  }
 823
 824  if((buffer->left == 0) || (buffer->flags & USB_BUFFER_PACKET_END)) {
 825    buffer->flags &= ~USB_BUFFER_SUBMITTED;
 826    if(buffer->flags & USB_BUFFER_NOTIFY) {
 827      res |= USB_READ_NOTIFY;
 828    }
 829    buffer = buffer->next;
 830  } else {
 831    if(short_packet) {
 832      if(buffer->left && !(buffer->flags & USB_BUFFER_SHORT_END)) {
 833        buffer->flags |= USB_BUFFER_FAILED;
 834        res |= USB_READ_FAIL;
 835      }
 836      buffer->flags &= ~USB_BUFFER_SUBMITTED;
 837      if(buffer->flags & USB_BUFFER_NOTIFY) {
 838        res |= USB_READ_NOTIFY;
 839      }
 840      buffer = buffer->next;
 841    }
 842  }
 843
 844  usb_endpoints[hw_ep].buffer = buffer;
 845
 846  return res;
 847}
 848/*---------------------------------------------------------------------------*/
 849static uint8_t
 850ep0_get_setup_pkt(void)
 851{
 852  uint8_t res;
 853  usb_buffer *buffer =
 854    skip_buffers_until(usb_endpoints[0].buffer, USB_BUFFER_SETUP,
 855                       USB_BUFFER_SETUP, &res);
 856
 857  usb_endpoints[0].buffer = buffer;
 858
 859  if(!buffer || buffer->left < 8) {
 860    return USB_READ_BLOCK;
 861  }
 862
 863  read_hw_buffer(buffer->data, 0, 8);
 864  buffer->left -= 8;
 865
 866  buffer->flags &= ~USB_BUFFER_SUBMITTED;
 867  if(buffer->flags & USB_BUFFER_NOTIFY) {
 868    res |= USB_READ_NOTIFY;
 869  }
 870
 871  if(buffer->data[6] || buffer->data[7]) {
 872    REG(USB_CS0) |= USB_CS0_CLR_OUTPKT_RDY;
 873    ep0status = buffer->data[0] & 0x80 ? EP_STATUS_TX : EP_STATUS_RX;
 874  }
 875
 876  buffer->data += 8;
 877
 878  usb_endpoints[0].buffer = buffer->next;
 879
 880  return res;
 881}
 882/*---------------------------------------------------------------------------*/
 883static uint8_t
 884ep0_get_data_pkt(void)
 885{
 886  uint8_t res = 0;
 887  uint8_t short_packet = 0;
 888  usb_buffer *buffer = usb_endpoints[0].buffer;
 889  uint8_t len = REG(USB_CNT0);
 890
 891  if(!buffer) {
 892    return USB_READ_BLOCK;
 893  }
 894
 895  if(buffer->flags & (USB_BUFFER_SETUP | USB_BUFFER_IN)) {
 896    uint8_t temp;
 897
 898    buffer->flags |= USB_BUFFER_FAILED;
 899    buffer->flags &= ~USB_BUFFER_SUBMITTED;
 900    if(buffer->flags & USB_BUFFER_NOTIFY) {
 901      res |= USB_READ_NOTIFY;
 902    }
 903    /* Flush the fifo */
 904    while(len--) {
 905      temp = REG(USB_F0);
 906    }
 907    usb_endpoints[0].buffer = buffer->next;
 908    /* Force data stage end */
 909    REG(USB_CS0) |= USB_CS0_CLR_OUTPKT_RDY | USB_CS0_DATA_END;
 910
 911    ep0status = EP_STATUS_IDLE;
 912    return res;
 913  }
 914
 915  if(get_receive_capacity(buffer) < len) {
 916    /* Wait until we queue more buffers */
 917    return USB_READ_BLOCK;
 918  }
 919
 920  if(len < usb_endpoints[0].xfer_size) {
 921    short_packet = 1;
 922  }
 923
 924  res = fill_buffers(buffer, 0, len, short_packet);
 925
 926  if(short_packet) {
 927    /* The usb-core will send a status packet, we will release the fifo at this stage */
 928    ep0status = EP_STATUS_IDLE;
 929  } else {
 930    REG(USB_CS0) |= USB_CS0_CLR_OUTPKT_RDY;
 931  }
 932  return res;
 933}
 934/*---------------------------------------------------------------------------*/
 935static uint8_t
 936ep0_tx(void)
 937{
 938  usb_buffer *buffer = usb_endpoints[0].buffer;
 939  unsigned int len = usb_endpoints[0].xfer_size;
 940  uint8_t data_end = 0;
 941  uint8_t res = 0;
 942
 943  /* If TX Fifo still busy or ep0 not in TX data stage don't do anything */
 944  if((REG(USB_CS0) & USB_CS0_INPKT_RDY) || (ep0status != EP_STATUS_TX)) {
 945    return 0;
 946  }
 947
 948  if(!buffer) {
 949    return 0;
 950  }
 951
 952  if(!(buffer->flags & USB_BUFFER_IN)) {
 953    /* We should TX but queued buffer is in RX */
 954    return 0;
 955  }
 956
 957  while(buffer) {
 958    unsigned int copy;
 959
 960    if(buffer->left < len) {
 961      copy = buffer->left;
 962    } else {
 963      copy = len;
 964    }
 965
 966    len -= copy;
 967    buffer->left -= copy;
 968    write_hw_buffer(0, buffer->data, copy);
 969    buffer->data += copy;
 970    if(buffer->left == 0) {
 971      if(buffer->flags & USB_BUFFER_SHORT_END) {
 972        if(len == 0) {
 973          break;                // We keep the buffer in queue so we will send a ZLP next time.
 974        } else {
 975          data_end = 1;
 976          len = 0;              // Stop looking for more data to send
 977        }
 978      }
 979      buffer->flags &= ~USB_BUFFER_SUBMITTED;
 980      if(buffer->flags & USB_BUFFER_NOTIFY) {
 981        res |= USB_WRITE_NOTIFY;
 982      }
 983      buffer = buffer->next;
 984    }
 985    if(len == 0) {
 986      break;                    // FIFO is full, send packet.
 987    }
 988  }
 989  if(len) {
 990    data_end = 1;
 991  }
 992  usb_endpoints[0].buffer = buffer;
 993
 994  /*
 995   * Workaround the fact that the usb controller do not like to have DATA_END
 996   * set after INPKT_RDY for the last packet. Thus if no more is in the queue
 997   * set DATA_END
 998   */
 999  if(data_end || !buffer) {
1000    ep0status = EP_STATUS_IDLE;
1001    REG(USB_CS0) |= USB_CS0_INPKT_RDY | USB_CS0_DATA_END;
1002  } else {
1003    REG(USB_CS0) |= USB_CS0_INPKT_RDY;
1004  }
1005
1006  return res;
1007}
1008/*---------------------------------------------------------------------------*/
1009static void
1010ep0_interrupt_handler(void)
1011{
1012  uint8_t cs0;
1013  uint8_t res;
1014
1015  REG(USB_INDEX) = 0;
1016  cs0 = REG(USB_CS0);
1017  if(cs0 & USB_CS0_SENT_STALL) {
1018    /* Ack the stall */
1019    REG(USB_CS0) = 0;
1020    ep0status = EP_STATUS_IDLE;
1021  }
1022  if(cs0 & USB_CS0_SETUP_END) {
1023    /* Clear it */
1024    REG(USB_CS0) = USB_CS0_CLR_SETUP_END;
1025    ep0status = EP_STATUS_IDLE;
1026  }
1027
1028  if(cs0 & USB_CS0_OUTPKT_RDY) {
1029    if(ep0status == EP_STATUS_IDLE) {
1030      res = ep0_get_setup_pkt();
1031    } else {
1032      res = ep0_get_data_pkt();
1033    }
1034
1035    if(res & USB_READ_NOTIFY) {
1036      notify_ep_process(&usb_endpoints[0], USB_EP_EVENT_NOTIFICATION);
1037    }
1038    if(res & USB_READ_BLOCK) {
1039      return;
1040    }
1041  }
1042
1043  res = ep0_tx();
1044
1045  if(res & USB_WRITE_NOTIFY) {
1046    notify_ep_process(&usb_endpoints[0], USB_EP_EVENT_NOTIFICATION);
1047  }
1048}
1049/*---------------------------------------------------------------------------*/
1050static uint8_t
1051ep_tx(uint8_t ep_hw)
1052{
1053  unsigned int len;
1054  uint8_t res = 0;
1055  usb_endpoint_t *ep = EP_STRUCT(ep_hw);
1056
1057  len = ep->xfer_size;
1058
1059  if(ep->halted) {
1060    return 0;
1061  }
1062
1063  if(!ep->buffer || !(ep->buffer->flags & USB_BUFFER_IN)) {
1064    return 0;
1065  }
1066
1067  while(ep->buffer) {
1068    unsigned int copy;
1069
1070    if(ep->buffer->left < len) {
1071      copy = ep->buffer->left;
1072    } else {
1073      copy = len;
1074    }
1075
1076    len -= copy;
1077    ep->buffer->left -= copy;
1078
1079    /*
1080     * Delay somewhat if the previous packet has not yet left the IN FIFO,
1081     * making sure the dog doesn't bark while we're waiting
1082     */
1083    while(REG(USB_CSIL) & USB_CSIL_INPKT_RDY) {
1084      watchdog_periodic();
1085    }
1086
1087    write_hw_buffer(EP_INDEX(ep_hw), ep->buffer->data, copy);
1088    ep->buffer->data += copy;
1089
1090    if(ep->buffer->left == 0) {
1091      if(ep->buffer->flags & USB_BUFFER_SHORT_END) {
1092        if(len == 0) {
1093          /* We keep the buffer in queue so we will send a ZLP next */
1094          break;
1095        } else {
1096          /* Stop looking for more data to send */
1097          len = 0;
1098        }
1099      }
1100      ep->buffer->flags &= ~USB_BUFFER_SUBMITTED;
1101      if(ep->buffer->flags & USB_BUFFER_NOTIFY) {
1102        res |= USB_WRITE_NOTIFY;
1103      }
1104      ep->buffer = ep->buffer->next;
1105    }
1106    if(len == 0) {
1107      /* FIFO full, send */
1108      break;
1109    }
1110  }
1111
1112  REG(USB_CSIL) |= USB_CSIL_INPKT_RDY;
1113
1114  return res;
1115}
1116/*---------------------------------------------------------------------------*/
1117static uint8_t
1118ep_get_data_pkt(uint8_t ep_hw)
1119{
1120  uint16_t pkt_len;
1121  uint8_t res;
1122  uint8_t short_packet = 0;
1123  usb_endpoint_t *ep = EP_STRUCT(ep_hw);
1124
1125  if(!ep->buffer) {
1126    return USB_READ_BLOCK;
1127  }
1128
1129  if(ep->buffer->flags & USB_BUFFER_HALT) {
1130    ep->halted = 1;
1131    if(!(REG(USB_CSOL) & USB_CSOL_SEND_STALL)) {
1132      REG(USB_CSOL) |= USB_CSOL_SEND_STALL;
1133    }
1134    return 0;
1135  }
1136
1137  /* Disambiguate UG CNTL bits */
1138  pkt_len = REG(USB_CNTL) | (REG(USB_CNTH) << 8);
1139  if(get_receive_capacity(ep->buffer) < pkt_len) {
1140    return USB_READ_BLOCK;
1141  }
1142
1143  if(pkt_len < ep->xfer_size) {
1144    short_packet = 1;
1145  }
1146
1147  res = fill_buffers(ep->buffer, ep_hw, pkt_len, short_packet);
1148
1149  REG(USB_CSOL) &= ~USB_CSOL_OUTPKT_RDY;
1150
1151  return res;
1152}
1153/*---------------------------------------------------------------------------*/
1154static void
1155out_ep_interrupt_handler(uint8_t ep_hw)
1156{
1157  uint8_t csl;
1158  uint8_t res;
1159  usb_endpoint_t *ep = EP_STRUCT(ep_hw);
1160
1161
1162  REG(USB_INDEX) = ep_hw;
1163  csl = REG(USB_CSOL);
1164
1165  if(csl & USB_CSOL_SENT_STALL) {
1166    REG(USB_CSOL) &= ~USB_CSOL_SENT_STALL;
1167  }
1168
1169  if(csl & USB_CSOL_OVERRUN) {
1170    /* We lost one isochronous packet */
1171    REG(USB_CSOL) &= ~USB_CSOL_OVERRUN;
1172  }
1173
1174  if(csl & USB_CSOL_OUTPKT_RDY) {
1175    res = ep_get_data_pkt(ep_hw);
1176
1177    if(res & USB_READ_NOTIFY) {
1178      notify_ep_process(ep, USB_EP_EVENT_NOTIFICATION);
1179    }
1180  }
1181}
1182/*---------------------------------------------------------------------------*/
1183static void
1184in_ep_interrupt_handler(uint8_t ep_hw)
1185{
1186  uint8_t csl;
1187#if USB_ARCH_WRITE_NOTIFY
1188  uint8_t res;
1189  usb_endpoint_t *ep = EP_STRUCT(ep_hw);
1190#endif
1191
1192  REG(USB_INDEX) = ep_hw;
1193  csl = REG(USB_CSIL);
1194
1195  if(csl & USB_CSIL_SENT_STALL) {
1196    REG(USB_CSIL) &= ~USB_CSIL_SENT_STALL;
1197  }
1198
1199  if(csl & USB_CSIL_UNDERRUN) {
1200    REG(USB_CSIL) &= ~USB_CSIL_UNDERRUN;
1201  }
1202
1203#if USB_ARCH_WRITE_NOTIFY
1204  if(!(csl & USB_CSIL_INPKT_RDY)) {
1205    res = ep_tx(ep_hw);
1206    if(res & USB_WRITE_NOTIFY) {
1207      notify_ep_process(ep, USB_EP_EVENT_NOTIFICATION);
1208    }
1209  }
1210#endif
1211}
1212/*---------------------------------------------------------------------------*/
1213void
1214usb_isr(void)
1215{
1216  uint8_t ep_in_if = REG(USB_IIF) & REG(USB_IIE);
1217  uint8_t ep_out_if = REG(USB_OIF) & REG(USB_OIE);
1218  uint8_t common_if = REG(USB_CIF) & REG(USB_CIE);
1219  uint8_t i;
1220
1221  ENERGEST_ON(ENERGEST_TYPE_IRQ);
1222
1223  if(ep_in_if) {
1224    /* EP0 flag is in the IN Interrupt Flags register */
1225    if(ep_in_if & USB_IIF_EP0IF) {
1226      ep0_interrupt_handler();
1227    }
1228    for(i = 1; i < 6; i++) {
1229      if(ep_in_if & EPxIF(i)) {
1230        in_ep_interrupt_handler(i);
1231      }
1232    }
1233  }
1234  if(ep_out_if) {
1235    for(i = 1; i < 6; i++) {
1236      if(ep_out_if & EPxIF(i)) {
1237        out_ep_interrupt_handler(i);
1238      }
1239    }
1240  }
1241  if(common_if & USB_CIF_RSTIF) {
1242    reset();
1243    notify_process(USB_EVENT_RESET);
1244  }
1245  if(common_if & USB_CIF_RESUMEIF) {
1246    notify_process(USB_EVENT_RESUME);
1247  }
1248  if(common_if & USB_CIF_SUSPENDIF) {
1249    notify_process(USB_EVENT_SUSPEND);
1250  }
1251
1252  ENERGEST_OFF(ENERGEST_TYPE_IRQ);
1253}
1254/*---------------------------------------------------------------------------*/
1255
1256/** @} */