Logo Search packages:      
Sourcecode: faumachine version File versions  Download package

arch_usb_controller.c

/* $Id: arch_usb_controller.c,v 1.151 2009-01-28 12:59:16 potyra Exp $ 
 *
 * Copyright (C) 2005-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

/* This is an implementation of the Intel UHCI. */

#define USB_NUM_PORTS   2           /* UHCI root hub has 2 ports */

#define USB_SENDBUFFER_SIZE 0x500
#define USB_NUM_VISITED_QHS 10

#ifdef STATE

struct {
      /*
       * Config Space
       */
      uint32_t config_space[64];

      /*
       * I/O Space
       */
      /* USB Command Register (16 Bit, R/W, WORD writeable only) */
      uint16_t usbcmd;

      /* USB Status (16 Bit, R/WC) */
      uint16_t usbsts;

      /* USB Interrupt Enable (16 Bit, R/W) */
      uint16_t usbintr;

      /* Frame Number (16 Bit, R/W, WORD writeable only, 15:11 reserved) */
      uint16_t frnum;

      /* Frame List Base Address (32 Bit, R/W, 11:0 reserved) */
      uint32_t flbaseadd;

      /* Start Of Frame Modify (8 Bit, R/W, 7 reserved) */
      uint8_t sofmod;

      /* Port Status And Control (16 Bit, R/W, WORD writeable only) */
      uint16_t portsc[USB_NUM_PORTS];

      /* time of the most recent timer event */
      unsigned long long tsc_passed;
      /* time between timer events when USBCMD_RUN_STOP is STOP */
      unsigned long long tsc_step_idle;
      /* time between timer events when USBCMD_RUN_STOP is RUN */
      unsigned long long tsc_step_busy;

      unsigned char global_reset;   /* currently in global reset mode? */

      struct {
            int speed;
            char reset;
      } portstate[USB_NUM_PORTS];

      /* name -- for debugging purposes */
      char name[10];

      /* schedule state */
      char working;     /* currently working on a schedule */

      char timeout_interrupt; /* trigger interrupt on timeout/crc error */
      char ioc;   /* trigger interrupt on completion at frame end */
      char spd;   /* trigger interrupt on short packet detection at frame end */
      char babble_interrupt, stalled_interrupt, data_buffer_error_interrupt,
           bit_stuff_error_interrupt, hc_process_error_interrupt,
           host_system_error_interrupt;   /* non-maskable interrupts */

      /* schedule list traversal state, cf. UHCI, Revision 1.1, chapter 3.4.2 "Transfer Queueing" */
      /* are we in a queue context? */
      char q_context;

      /* Pointer to QH */
      uint32_t qhp;

      /* QH */
      struct {
            uint32_t qhlp;
            char qh_q, qh_t;
            uint32_t qelp;
            char qe_vf, qe_q, qe_t;
      } qh;

      /* Pointer to TD */
      uint32_t tdp;

      /* TD */
      struct {
            uint32_t td[4];   /* content to avoid re-read */

            /* TD link pointer: points to next TD to be executed */
            uint32_t tdlp;
            char td_vf, td_q, td_t;

            unsigned char c_err, actlen, endpt, addr, pid;
            unsigned int maxlen;
      } td;

      unsigned tds_sent;
      
      unsigned char sendbuffer[USB_SENDBUFFER_SIZE];

      /*
       * count queue headers: bandwidth reclamation leads to the last QH
       * pointing back in the queue, in effect creating an endless loop; real
       * hardware terminates the frame if the 1ms time frame is over, we try
       * to emulate this by counting QHs.
       *
       * This is only a fallback if the visited_qhs (see below) mechanism
       * fails in case the guest OS has some weird QH arrangement.
       */
      unsigned long qh_count;

      /*
       * Store the first USB_NUM_VISITED_QHS addresses to QH structures to
       * avoid looping in memory at all. As we execute multiple TDs at once
       * regardless of Vf (width/breadth first), the looping mechanism is
       * useless anyways.
       */
      uint32_t visited_qhs[USB_NUM_VISITED_QHS];

      /* number of scheduled time_events */
      unsigned int timer_scheduled;
} NAME;

#endif /* STATE */
#ifdef BEHAVIOR

#include "pci.h"
#include "lib_usb.h" /* FIXME */

/*----------------------------DEBUG-------------------------------------------*/
/* binary OR these in USB_DEBUGMASK */
#define USB_DEBUG_WARNINGS          (1 << 0)
#define USB_DEBUG_CONFSPACE         (1 << 1)
#define USB_DEBUG_IO                (1 << 2)
#define USB_DEBUG_CIM               (1 << 3)
#define USB_DEBUG_DUMP_PACKETS            (1 << 4)
#define USB_DEBUG_FRAME_LIST        (1 << 5)
#define USB_DEBUG_DUMP_TDS          (1 << 6)
#define USB_DEBUG_IRQ               (1 << 7)
#define USB_DEBUG_TIMEOUTS          (1 << 8)

#define USB_DEBUGMASK 0x0000

#define USB_LOG_TYPE    "USB"
/*----------------------------------------------------------------------------*/

/* The following definitions are adopted from the chipset manual */

/* USB Host Controller PCI Configuration Registers */
#define C82371AB_USB_USBBA    0x20  /* 32 bits */
#define C82371AB_USB_SBRNUM   0x60  /*  8 bits */
#define C82371AB_USB_LEGSUP   0xC0  /* 16 bits */
#define C82371AB_USB_MISCSUP  0xFF  /*  8 bits */

/* USB Host Controller IO Space Registers */
#define C82371AB_USB_USBCMD   0x00  /* 16 bits */
#define C82371AB_USB_USBSTS   0x02  /* 16 bits */
#define C82371AB_USB_USBINTR  0x04  /* 16 bits */
#define C82371AB_USB_FRNUM    0x06  /* 16 bits */
#define C82371AB_USB_FLBASEADD      0x08  /* 32 bits */
#define C82371AB_USB_SOFMOD   0x0C  /*  8 bits */
#define C82371AB_USB_PORTSC0  0x10  /* 16 bits */
#define C82371AB_USB_PORTSC1  0x12  /* 16 bits */

/* Possible values for USBCMD */
#define USBCMD_RESERVED             0xFF00
#define USBCMD_MAX_PACKET           (1<<7)
#define USBCMD_CONFIGURE_FLAG       (1<<6)
#define USBCMD_SOFTWARE_DEBUG       (1<<5)
#define USBCMD_FORCE_GLOBAL_RESUME  (1<<4)
#define USBCMD_ENTER_GLOBAL_SUSPEND (1<<3)
#define USBCMD_GLOBAL_RESET         (1<<2)
#define USBCMD_HOST_CONTROLLER_RESET      (1<<1)
#define USBCMD_RUN_STOP             (1<<0)

/* Possible values for USBSTS */
#define USBSTS_RESERVED             0xFFC0
#define USBSTS_HC_HALTED            (1<<5)
#define USBSTS_HC_PROCESS_ERROR           (1<<4)
#define USBSTS_HOST_SYSTEM_ERROR    (1<<3)
#define USBSTS_RESUME_DETECT        (1<<2)
#define USBSTS_ERROR_INTERRUPT            (1<<1)
#define USBSTS_INTERRUPT            (1<<0)

/* Possible values for USBINTR */
#define USBINTR_RESERVED            0xFFF0
#define USBINTR_SPIE                (1<<3)
#define USBINTR_IOC                 (1<<2)
#define USBINTR_RIE                 (1<<1)
#define USBINTR_TIE                 (1<<0)

/* Possible values for FRNUM */
#define FRNUM_RESERVED              0xF800
#define FRNUM_FRNUM                 (~FRNUM_RESERVED)
#define FRNUM_FL_INDEX              0x03FF

/* Possible values for FLBASEADD */
#define FLBASEADD_FLBASEADD         0xFFFFF000UL
#define FLBASEADD_RESERVED          (~FLBASEADD_FLBASEADD)

/* Possible values for SOFMOD */
#define SOFMOD_RESERVED             (1<<7)
#define SOFMOD_SOF_TIMING           0x7F

/* Possible values for PORTSC0/1 */
#define PORTSC_RESERVED_CLEAR       0xE000      /* write as 0 */
#define PORTSC_SUSPEND              (1<<12)     /* R/W */
#define PORTSC_OVERCURRENT_INDICATOR_CHANGE     (1<<11)     /* R/WC */
#define PORTSC_OVERCURRENT_INDICATOR      (1<<10)     /* RO */
#define PORTSC_PORT_RESET           (1<<9)      /* R/W */
#define PORTSC_LOWSPEED_DEVICE_ATTACHED   (1<<8)      /* RO */
#define PORTSC_RESERVED_SET         (1<<7)      /* RO, read as 1 */
#define PORTSC_RESUME_DETECT        (1<<6)      /* R/W */
#define PORTSC_LINE_STATUS_DMINUS   (1<<5)      /* RO */
#define PORTSC_LINE_STATUS_DPLUS    (1<<4)      /* RO */
#define PORTSC_PORT_ENABLE_DISABLE_CHANGE (1<<3)      /* R/WC */
#define PORTSC_PORT_ENABLE          (1<<2)      /* R/W */
#define PORTSC_CONNECT_STATUS_CHANGE      (1<<1)      /* R/WC */
#define PORTSC_CURRENT_CONNECT_STATUS     (1<<0)      /* RO */

#define SZ_USB_IOSPACE        32    /* I/O space size */

#define USB_TIMER_FREQ_IDLE   10    /* used when USBCMD_RUN_STOP is STOP */
#define USB_TIMER_FREQ_BUSY   100   /* used when USBCMD_RUN_STOP is RUN, should be 1000 */

/* in-memory data structures (cf. UHCI, Rev. 1.1, Section 3 "Data Structures") */
/* Frame List Pointer */
#define USB_MEM_FLP_FLP       0xFFFFFFF0UL      /* Frame List Pointer (FLP) */
#define USB_MEM_FLP_RESERVED  0x0000000CUL      /* write as 0 */
#define USB_MEM_FLP_QH        (1<<1)            /* QH/TD Select (Q) */
#define USB_MEM_FLP_TERMINATE (1<<0)            /* TERMINATE (T) */

/* Transfer Descriptor */
/* TD Link Pointer */
#define USB_MEM_TD_0_LP       0xFFFFFFF0UL      /* Link Pointer (LP) */
#define USB_MEM_TD_0_RESERVED (1<<3)            /* write as 0 */
#define USB_MEM_TD_0_VF       (1<<2)            /* Depth/Breadth Select (Vf) */
#define USB_MEM_TD_0_QH       (1<<1)            /* QH/TD Select (Q) */
#define USB_MEM_TD_0_TERMINATE      (1<<0)            /* TERMINATE (T) */
/* TD Control and Status */
#define USB_MEM_TD_1_RESERVED 0xC001F800UL      /* bits 31,30,16,15:11 */
#define USB_MEM_TD_1_SPD      (1<<29)           /* Short Packet Detect (SPD) */
#define USB_MEM_TD_1_C_ERR    (1<<28|1<<27)     /* Error Counter (ERR_C) */
#define USB_MEM_TD_1_C_ERR_SHIFT    27
#define USB_MEM_TD_1_LS       (1<<26)           /* Low Speed Device (LS) */
#define USB_MEM_TD_1_IOS      (1<<25)           /* Isochronous Select (IOS) */
#define USB_MEM_TD_1_IOC      (1<<24)           /* Interrupt on Complete (IOC) */
#define USB_MEM_TD_1_STATUS   0x00FF0000  /* Status */
#define USB_MEM_TD_1_STATUS_ACTIVE        (1<<23)     /* Status Active */
#define USB_MEM_TD_1_STATUS_STALLED       (1<<22)     /* Status Stalled */
#define USB_MEM_TD_1_STATUS_DATA_BUFFER_ERROR   (1<<21)     /* Status Data Buffer Error */
#define USB_MEM_TD_1_STATUS_BABBLE_DETECTED     (1<<20)     /* Status Babble Detected */
#define USB_MEM_TD_1_STATUS_NAK_RECEIVED  (1<<19)     /* Status NAK Received */
#define USB_MEM_TD_1_STATUS_CRC_TIMEOUT_ERROR   (1<<18)     /* Status CRC/Time Out Error */
#define USB_MEM_TD_1_STATUS_BITSTUFF_ERROR      (1<<17)     /* Status Bitstuff Error */
#define USB_MEM_TD_1_ACTLEN         0x000007FF  /* Actual Length (ActLen) */
/* TD Token */
#define USB_MEM_TD_2_MAXLEN         0xFFE00000  /* Maximum Length (MaxLen) (31:21) */
#define USB_MEM_TD_2_MAXLEN_SHIFT   21
#define USB_MEM_TD_2_MAXLEN_WRAP    0x7FF       /* wrap values at this maximum value */
#define USB_MEM_TD_2_MAXLEN_ILLEGAL 0x500       /* everything >= 0x500 is illegal */
#define USB_MEM_TD_2_RESERVED (1<<20)           /* Reserved */
#define USB_MEM_TD_2_D        (1<<19)           /* Data Toggle (D) */
#define USB_MEM_TD_2_ENDPT    0x00078000UL      /* Endpoint (EndPt) (18:15) */
#define USB_MEM_TD_2_ENDPT_SHIFT    15
#define USB_MEM_TD_2_DEV_ADDR 0x00007F00UL      /* Device Address (14:8) */
#define USB_MEM_TD_2_DEV_ADDR_SHIFT 8
#define USB_MEM_TD_2_PID      0x000000FFUL      /* Packet Identification (PID) (7:0) */
/* TD Buffer Pointer -- extends over whole DWORD 3 */
/* DWORDS 4-7 are reserved for use by software */

/* Queue Head (QH) */
/* Queue Head Link Pointer */
#define USB_MEM_QH_0_QHLP     0xFFFFFFF0UL      /* Queue Head Link Pointer (QHLP) (31:4) */
#define USB_MEM_QH_0_RESERVED 0x0000000CUL      /* write as 0 (3:2) */
#define USB_MEM_QH_0_QH       (1<<1)            /* QH/TD Select (Q) */
#define USB_MEM_QH_0_TERMINATE      (1<<0)            /* TERMINATE (T) */
/* Queue Element Link Pointer */
#define USB_MEM_QH_1_QELP     0xFFFFFFF0UL      /* Queue Element Link Pointer (QELP) (31:4) */
#define USB_MEM_QH_1_RESERVED (1<<3)            /* write as 0 */
#define USB_MEM_QH_1_VF       (1<<2)            /* reserved for copied TD VF */
#define USB_MEM_QH_1_QH       (1<<1)            /* QH/TD Select (Q) */
#define USB_MEM_QH_1_TERMINATE      (1<<0)            /* TERMINATE (T) */

/* parameter 2 of NAME_(advance_queue)() */
#define USB_QUEUE_NO_ADVANCE  0
#define USB_QUEUE_ADVANCE     1

/* see arch_usb_controller.h -> qh_count */
#define USB_MAX_QH_PER_FRAME  30

#define USB_MAX_TDS_AT_ONCE   1

/*
 * how many frames to skip in case no TD was successful in one frame
 */
#define USB_FRAME_HOP_COUNT   10


/* forward declarations */
static void
NAME_(connect_device)(struct cpssp *css, int usb_portnr);
static void 
NAME_(packet_timeout)(struct cpssp *css);
static void
NAME_(timer_event)(void *datap);

static void
NAME_(set_irq)(struct cpssp *css, int value)
{
#if USB_DEBUGMASK & USB_DEBUG_IRQ
      if (value) {
            faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
                  "%s: IRQ line 1, USBSTS is %02X\n",
                  __FUNCTION__, css->NAME.usbsts);
      } else {
            faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
                  "%s: IRQ line 0\n", __FUNCTION__);
      }
#endif
      usb_irqrq_out_set(css, value);
}

static void
NAME_(reset)(struct cpssp *css)
{
      int i;

      /* initialize I/O space registers */
      css->NAME.usbcmd        = 0x0;
      css->NAME.usbsts        = 0x0;
      css->NAME.usbintr       = 0x0;
      css->NAME.frnum               = 0x0;
      css->NAME.flbaseadd           = 0x0;
      css->NAME.sofmod        = 0x40;
      for (i = 0; i < USB_NUM_PORTS; i++) {
            css->NAME.portsc[i]     = 0x80;
      }

      css->NAME.global_reset = 0;
}

/*----------------------------I/O interface-----------------------------------*/
static void
NAME_(_inw_core)(struct cpssp *css, uint16_t *valuep, uint16_t port)
{
      switch (port) {
      case C82371AB_USB_USBCMD:     /* USB Command */
            *valuep = css->NAME.usbcmd;
            break;

      case C82371AB_USB_USBSTS:     /* USB Status */
            *valuep = css->NAME.usbsts;
            break;

      case C82371AB_USB_USBINTR:    /* USB Interrupt Enable */
            *valuep = css->NAME.usbintr;
            break;

      case C82371AB_USB_FRNUM:      /* Frame Number */
            *valuep = css->NAME.frnum;
            break;

      case C82371AB_USB_FLBASEADD:  /* Frame List Base Address (Low) */
            *valuep = (css->NAME.flbaseadd >> 0) & 0x0000ffff;
            break;

      case C82371AB_USB_FLBASEADD + 2:/* Frame List Base Address (High) */
            *valuep = (css->NAME.flbaseadd >> 16) & 0x0000ffff;
            break;

      case C82371AB_USB_SOFMOD:     /* Start Of Frame Modify */
            *valuep = css->NAME.sofmod;
            break;

      case C82371AB_USB_PORTSC0:    /* USB Port Status And Control */
      case C82371AB_USB_PORTSC1:
            *valuep = css->NAME.portsc[(port - C82371AB_USB_PORTSC0) >> 1];
            break;

      case C82371AB_USB_PORTSC1 + 2:      /* non-existent port 2 */
            *valuep = 0xff7f; /* bit 7 = 0 makes linux kernel stop
                               * scanning for more ports */
            break;

      default:
            /*
             * Reading from unknown I/O address
             */
            *valuep = 0xff7f;
#if USB_DEBUGMASK & USB_DEBUG_WARNINGS
            faum_log(FAUM_LOG_WARNING, USB_LOG_TYPE, css->NAME.name,
                  "reading from I/O offset 0x%02x\n", port);
#endif
            break;
      }
}

static void
NAME_(_inb)(struct cpssp *css, uint8_t *valuep, uint16_t port)
{
      uint16_t val;

      NAME_(_inw_core)(css, &val, port & ~1);
      if (port & 1) {         /* high byte */
            *valuep = (val >> 8) & 0x00ff;
      } else {          /* low byte */
            *valuep = (val >> 0) & 0x00ff;
      }

#if USB_DEBUGMASK & USB_DEBUG_IO
      faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
            "%s: port=0x%02x val=0x%02x\n",
            __FUNCTION__, port, *valuep);
#endif
}

static void
NAME_(_inw)(struct cpssp *css, uint16_t *valuep, uint16_t port)
{
      NAME_(_inw_core)(css, valuep, port);

#if USB_DEBUGMASK & USB_DEBUG_IO
      faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
            "%s: port=0x%02x val=0x%04x\n",
            __FUNCTION__, port, *valuep);
#endif
}

static void
NAME_(_inl)(struct cpssp *css, uint32_t *valuep, uint16_t port)
{
      uint16_t low;
      uint16_t high;

      NAME_(_inw_core)(css, &low, port);
      NAME_(_inw_core)(css, &high, port + 2);
      *valuep = low | (high << 16);

#if USB_DEBUGMASK & USB_DEBUG_IO
      faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
            "%s: port=0x%02x val=0x%08lx\n",
            __FUNCTION__, (unsigned) port, (unsigned long) *valuep);
#endif
}

static void
NAME_(_outb_core)(struct cpssp *css, uint8_t value, uint16_t port)
{
      switch (port) {
      case C82371AB_USB_USBCMD:     /* USB Command (R/W), WORD writeable only */
      case C82371AB_USB_USBCMD + 1:
            goto word_writeable_only;

      case C82371AB_USB_USBSTS:     /* USB Status (R/WC) (Bits 0-7) */
            /* FIXME sihoschi: Bits 15-6 are reserved */
            css->NAME.usbsts &= ~value;

            /* FIXME: Linux Kernel sources tell this:
             * 'Contrary to the UHCI specification, the "HC Halted" status
             * bit is persistent: it is RO, not R/WC.'
             */

            /* are all interrupt reasons cleared? */
            if ((css->NAME.usbsts & 0x1F) == 0) {
                  NAME_(set_irq)(css, 0);
            }
            break;

      case C82371AB_USB_USBSTS + 1: /* USB Status (R/WC) (Bits 8-15) */
            /* reserved, nothing to do */
            break;

      case C82371AB_USB_USBINTR:    /* USB Interrupt Enable (Bits 0-7) */
            /* FIXME sihoschi: Bits 15-4 are reserved */
            css->NAME.usbintr = value;
            break;

      case C82371AB_USB_USBINTR + 1:      /* USB Interrupt Enable (Bits 8-15) */
            /* reserved, nothing to do */
            break;

      case C82371AB_USB_FRNUM:      /* Frame Number (Bits 0-7) */
      case C82371AB_USB_FRNUM + 1:  /* Frame Number (Bits 8-15) */
            goto word_writeable_only;

      case C82371AB_USB_FLBASEADD:  /* Frame List Base Address (Bits 0-7) */
#if USB_DEBUGMASK & USB_DEBUG_WARNINGS
            if (value != 0x00) {
                  faum_log(FAUM_LOG_WARNING, USB_LOG_TYPE,
                        css->NAME.name,
                        "%s: port=0x%02x val=0x%02x "
                        "although reserved and zero\n",
                        __FUNCTION__, port, value);
            }
#endif
            value = 0x0;
            css->NAME.flbaseadd &= 0xFFFFFF00UL;
            css->NAME.flbaseadd |= value;
            break;

      case C82371AB_USB_FLBASEADD + 1:/* Frame List Base Address (Bits 8-15) */
#if USB_DEBUGMASK & USB_DEBUG_WARNINGS
            if (value & 0x07) {
                  faum_log(FAUM_LOG_WARNING, USB_LOG_TYPE,
                        css->NAME.name,
                        "%s: port=0x%02x val=0x%02x "
                        "although reserved and zero\n",
                        __FUNCTION__, port, value);
            }
#endif
            value &= ~0x07;
            css->NAME.flbaseadd &= 0xFFFF00FFUL;
            css->NAME.flbaseadd |= value << 8;
            break;

      case C82371AB_USB_FLBASEADD + 2:/* Frame List Base Address (Bits 16-23) */
            css->NAME.flbaseadd &= 0xFF00FFFFUL;
            css->NAME.flbaseadd |= value << 16;
            break;

      case C82371AB_USB_FLBASEADD + 3:/* Frame List Base Address (Bits 24-31) */
            css->NAME.flbaseadd &= 0x00FFFFFFUL;
            css->NAME.flbaseadd |= value << 24;
            break;

      case C82371AB_USB_SOFMOD:     /* Start Of Frame Modify */
#if USB_DEBUGMASK & USB_DEBUG_WARNINGS
            if (value & SOFMOD_RESERVED) {
                  faum_log(FAUM_LOG_WARNING, USB_LOG_TYPE,
                        css->NAME.name,
                        "%s: port=0x%02x val=0x%02x "
                        "although reserved and zero\n",
                        __FUNCTION__, port, value);
            }
#endif
            value &= SOFMOD_SOF_TIMING;
            css->NAME.sofmod = value;
            break;

      case C82371AB_USB_PORTSC0:    /* USB Port Status And Control */
      case C82371AB_USB_PORTSC0 + 1:
      case C82371AB_USB_PORTSC1:
      case C82371AB_USB_PORTSC1 + 1:
            goto word_writeable_only;

      word_writeable_only:
#if USB_DEBUGMASK & USB_DEBUG_WARNINGS
            faum_log(FAUM_LOG_WARNING, USB_LOG_TYPE, css->NAME.name,
                  "writing to I/O offset 0x%02x (word writeable only)\n",
                  port);
#endif
            break;

      default:
            /*
             * Writing to unknown I/O address
             */
#if USB_DEBUGMASK & USB_DEBUG_WARNINGS
            faum_log(FAUM_LOG_WARNING, USB_LOG_TYPE, css->NAME.name,
                  "writing to I/O offset 0x%02x\n", port);
#endif
            break;
      }
}

static void
NAME_(_outw_core)(struct cpssp *css, uint16_t value, uint16_t port)
{
      uint16_t old_val;
      uint16_t mask;
      unsigned int usb_portnr;

      switch (port) {
      case C82371AB_USB_USBCMD:     /* USB Command (R/W), WORD writeable only */
#if USB_DEBUGMASK & USB_DEBUG_WARNINGS
            if (value & USBCMD_RESERVED) {
                  faum_log(FAUM_LOG_WARNING, USB_LOG_TYPE,
                        css->NAME.name,
                        "%s: port=0x%02x val=0x%04x "
                        "although reserved and zero\n",
                        __FUNCTION__, port, value);
            }
#endif
            css->NAME.usbcmd = value;

            /* HCReset */
            if (value & USBCMD_HOST_CONTROLLER_RESET) {
                  /* this implicitly clears the HCReset bit */
                  NAME_(reset)(css);

                  /* TODO: terminate transactions in progress? */

                  /* connect status change  and  port enable/disable
                   * change  of every port is set now */
                  for (usb_portnr = 0; usb_portnr < USB_NUM_PORTS; usb_portnr++) {
                        css->NAME.portsc[usb_portnr] |=
                                PORTSC_PORT_ENABLE_DISABLE_CHANGE
                              | PORTSC_CONNECT_STATUS_CHANGE;
                        /* FIXME: reconnect devices "after 64 bit-times"? */
                        NAME_(connect_device)(css, usb_portnr);
                  }
            }

            /* GReset... */
            if (value & USBCMD_GLOBAL_RESET) {        /* ... was SET */
                  css->NAME.global_reset = 1;
            } else if (css->NAME.global_reset) {      /* ... was CLEARED */
                  css->NAME.global_reset = 0;
                  /* reset after the guaranteed 10ms minimum global_reset delay */
                  NAME_(reset)(css);

                  /* reset everyone downstream */
                  for (usb_portnr = 0; usb_portnr < USB_NUM_PORTS; usb_portnr++) {
                        if (usb_portnr == 0) {
                              usb0_reset_set(css, 1);
                        } else { assert(usb_portnr == 1);
                              usb1_reset_set(css, 1);
                        }
                        NAME_(connect_device)(css, usb_portnr);
                  }
            }

            /* Run/Stop */
            if (value & USBCMD_RUN_STOP) {                  /* run */
                  /* clear corresponding status bit */
                  css->NAME.usbsts &= ~USBSTS_HC_HALTED;
                  /* TODO: maybe immediately start a new frame?
                   * problem: locking... */
            } else {                            /* stop */
                  /* set corresponding status bit */
                  css->NAME.usbsts |= USBSTS_HC_HALTED;

                  if (0 < css->NAME.tds_sent) {
                        NAME_(packet_timeout)(css);
                        css->NAME.tds_sent = 0;
                  }
            }

#if USB_DEBUGMASK & USB_DEBUG_WARNINGS
            /* FIXME: SWDEBUG completely ignored at the moment */
            if (value & USBCMD_SOFTWARE_DEBUG) {
                  faum_log(FAUM_LOG_WARNING, USB_LOG_TYPE,
                        css->NAME.name,
                        "unimplemented USBCMD bit set: %04x\n",
                        value);
            }
#endif
            break;

      case C82371AB_USB_FRNUM:      /* Frame Number */
            /* "This register cannot be written unless the Host Controller
             * is in the STOPPED state as indicated by the HCHalted bit
             * (USBSTS register). A write to this register while the
             * Run/Stop bit is set (USBCMD register) is ignored." */
            if (! (css->NAME.usbcmd & USBCMD_RUN_STOP)
             && css->NAME.usbsts & USBSTS_HC_HALTED) {
                  css->NAME.frnum = value & FRNUM_FRNUM;
            } else {
#if USB_DEBUGMASK & USB_DEBUG_WARNINGS
                  faum_log(FAUM_LOG_WARNING, USB_LOG_TYPE,
                        css->NAME.name,
                        "%s: port=0x%02x (FRNUM) val=0x%04x "
                        "but currently not writeable\n",
                        __FUNCTION__, port, value);
#endif
            }
            break;

      case C82371AB_USB_PORTSC0:    /* USB Port Status And Control */
      case C82371AB_USB_PORTSC1:
            usb_portnr = (port - C82371AB_USB_PORTSC0) >> 1;
            old_val = css->NAME.portsc[usb_portnr];

            /* R/W bits */
            mask = PORTSC_SUSPEND | PORTSC_PORT_RESET
                 | PORTSC_RESUME_DETECT | PORTSC_PORT_ENABLE;
            old_val &= ~mask;
            old_val |= value & mask;

            /* R/WC bits */
            mask = PORTSC_OVERCURRENT_INDICATOR_CHANGE
                 | PORTSC_PORT_ENABLE_DISABLE_CHANGE
                 | PORTSC_CONNECT_STATUS_CHANGE;
            old_val &= ~(value & mask);

            css->NAME.portsc[usb_portnr] = old_val;

            /* PORT_RESET... */
            /* FIXME: docs are ambiguous here? */
            if (old_val & PORTSC_PORT_RESET) { /* ... was SET */
                  css->NAME.portstate[usb_portnr].reset = 1;
                  css->NAME.portsc[usb_portnr] &=
                        ~(PORTSC_LOWSPEED_DEVICE_ATTACHED
                        | PORTSC_LINE_STATUS_DMINUS
                        | PORTSC_LINE_STATUS_DPLUS
                        | PORTSC_PORT_ENABLE_DISABLE_CHANGE
                        | PORTSC_PORT_ENABLE
                        | PORTSC_CONNECT_STATUS_CHANGE
                        | PORTSC_CURRENT_CONNECT_STATUS);
            } else if (css->NAME.portstate[usb_portnr].reset) { /* ... was CLEARED */
                  css->NAME.portstate[usb_portnr].reset = 0;

                  /* reset everyone on this port */
                  if (usb_portnr == 0) {
                        usb0_reset_set(css, 1);
                  } else { assert(usb_portnr == 1);
                        usb1_reset_set(css, 1);
                  }
                  NAME_(connect_device)(css, usb_portnr);
            }
            break;

      default:
            NAME_(_outb_core)(css, (value >> 0) & 0x00ff, port + 0);
            NAME_(_outb_core)(css, (value >> 8) & 0x00ff, port + 1);
            break;
      }

}

static void
NAME_(_outb)(struct cpssp *css, uint8_t value, uint16_t port)
{
#if USB_DEBUGMASK & USB_DEBUG_IO
      faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
            "%s: port=0x%02x val=0x%02x\n",
            __FUNCTION__, port, value);
#endif

      NAME_(_outb_core)(css, value, port);
}

static void
NAME_(_outw)(struct cpssp *css, uint16_t value, uint16_t port)
{
#if USB_DEBUGMASK & USB_DEBUG_IO
      faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
            "%s: port=0x%02x val=0x%04x\n",
            __FUNCTION__, port, value);
#endif

      NAME_(_outw_core)(css, value, port);
}

static void
NAME_(_outl)(struct cpssp *css, uint32_t value, uint16_t port)
{
#if USB_DEBUGMASK & USB_DEBUG_IO
      faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
            "%s: port=0x%02x val=0x%08lx\n",
            __FUNCTION__, (unsigned) port, (unsigned long) value);
#endif

      NAME_(_outw_core)(css, (value >>  0) & 0x0000FFFFUL, port + 0);
      NAME_(_outw_core)(css, (value >> 16) & 0x0000FFFFUL, port + 2);
}

/*
 * Shamelessly copied from ume100.c:
 * Auto-generate I/O handlers that check whether we are responsible or not
 * and in the former case pass on to the NAME__* functions.
 */
#define CREATEIOFUNC(iotype, vtype) \
static int \
NAME_(iotype)( \
      struct cpssp *css, \
      vtype val, \
      uint16_t port \
) \
{ \
      unsigned long io_base_addr = pci_getconfigl( \
                  css->NAME.config_space, \
                  PCI_BASE_ADDRESS_4) & ~1; \
      \
      if (io_base_addr <= port && port < io_base_addr + SZ_USB_IOSPACE \
          && pci_getconfigl(css->NAME.config_space, \
                              PCI_COMMAND) & PCI_COMMAND_IO) { \
            NAME_(_##iotype)(css, val, port - io_base_addr); \
            return 0; \
      } \
      return -1; \
}

CREATEIOFUNC(outb, uint8_t)
CREATEIOFUNC(outw, uint16_t)
CREATEIOFUNC(outl, uint32_t)
CREATEIOFUNC(inb, uint8_t *)
CREATEIOFUNC(inw, uint16_t *)
CREATEIOFUNC(inl, uint32_t *)

#undef CREATEIOFUNC

/*----------------------------Configuration Interface (PCI bus)---------------*/

static void
NAME_(_cwriteb)(struct cpssp *css, uint8_t val, unsigned long addr)
{
      uint8_t old_val;

      switch (addr) {
      case PCI_VENDOR_ID:     /* Vendor Identification */
      case PCI_VENDOR_ID + 1:
            goto read_only;

      case PCI_DEVICE_ID:     /* Device Identification */
      case PCI_DEVICE_ID + 1:
            goto read_only;

      case PCI_COMMAND: /* PCI Command (Bit 0-7). */
            /* Some bits are hardwired to '0'/'1'. */
            val &= ~(1 << 7); /* Reserved */
            val &= ~(1 << 6); /* Reserved */
            val &= ~(1 << 5); /* Reserved */
            val &= ~(1 << 4); /* Memory Write and Invalidate */
            val &= ~(1 << 3); /* Special Cycle Enable */
            val &= ~(1 << 1); /* Memory Space Enable */
            pci_setconfigb(css->NAME.config_space, addr, val);
            break;

      case PCI_COMMAND + 1:         /* PCI Command (Bit 8-15). */
            /* Some bits are hardwired to '0'. */
            val &= ~(1 << (15-8));  /* Reserved */
            val &= ~(1 << (14-8));  /* Reserved */
            val &= ~(1 << (13-8));  /* Reserved */
            val &= ~(1 << (12-8));  /* Reserved */
            val &= ~(1 << (11-8));  /* Reserved */
            val &= ~(1 << (10-8));  /* Reserved */
            val &= ~(1 << ( 9-8));  /* Fast Back-to-Back Enable */
            val &= ~(1 << ( 8-8));  /* Reserved */
            pci_setconfigb(css->NAME.config_space, addr, val);
            break;

      case PCI_STATUS:        /* PCI Device Status (Bit 0-7) */
            /* Some bits are hardwired to '0'/'1'. */
            val |=  (1 << 7); /* Fast Back to back Capable */
            val &= ~(1 << 6); /* Reserved */
            val &= ~(1 << 5); /* Reserved */
            val &= ~(1 << 4); /* Reserved */
            val &= ~(1 << 3); /* Reserved */
            val &= ~(1 << 2); /* Reserved */
            val &= ~(1 << 1); /* Reserved */
            val &= ~(1 << 0); /* Reserved */
            pci_setconfigb(css->NAME.config_space, addr, val);
            break;

      case PCI_STATUS + 1:          /* PCI Device Status (Bit 8-15) */
            /* Some bits are hardwired to '0'/'1'. */
            val &= ~(1 << (15-8));  /* Detected Parity Error */
            val &= ~(1 << (14-8));  /* SERR# Status */
            val &= ~(1 << (10-8));  /* DEVSEL# Timing Status */
            val |=  (1 << ( 9-8));
            val &= ~(1 << ( 8-8));  /* Data Parity Detected */

            /* Some bits are write-clear. */
            old_val = pci_getconfigb(css->NAME.config_space, addr);

            /* Master-Abort Status */
            if ((val & (1 << (13-8)))) {
                  val &= ~(1 << (13-8));
            } else {
                  val &= ~(1 << (13-8));
                  val |= old_val & (1 << (13-8));
            }
            /* Received Target-Abort Status */
            if ((val & (1 << (12-8)))) {
                  val &= ~(1 << (12-8));
            } else {
                  val &= ~(1 << (12-8));
                  val |= old_val & (1 << (12-8));
            }
            /* Signaled Target Abort Status */
            if ((val & (1 << (11-8)))) {
                  val &= ~(1 << (11-8));
            } else {
                  val &= ~(1 << (11-8));
                  val |= old_val & (1 << (11-8));
            }

            pci_setconfigb(css->NAME.config_space, addr, val);
            break;

      case PCI_CLASS_REVISION:      /* Revision Identification */
            goto read_only;

      case PCI_CLASS_PROG:          /* Reg. Level Programming Interface */
      case PCI_CLASS_DEVICE:        /* Device Class */
      case PCI_CLASS_DEVICE + 1:
            goto read_only;

      case PCI_LATENCY_TIMER:       /* Master Latency Timer */
            /* Some bits are hardwired to '0'. */
            val &= ~(1 << 3);
            val &= ~(1 << 2);
            val &= ~(1 << 1);
            val &= ~(1 << 0);

            pci_setconfigb(css->NAME.config_space, addr, val);
            break;

      case PCI_HEADER_TYPE:         /* Header Type */
            /*FALLTHROUGH*/
      case PCI_INTERRUPT_PIN:       /* Interrupt Pin */
            /* FIXME: BIOS writes to this one although read-only */
            /*FALLTHROUGH*/
      case C82371AB_USB_SBRNUM:     /* Serial Bus Release Number */

      read_only:
            /*
             * Don't write to read-only registers.
             * We assume a programming error in this case.
             */
            /* Nothing to do. */
#if USB_DEBUGMASK & USB_DEBUG_WARNINGS
            faum_log(FAUM_LOG_WARNING, USB_LOG_TYPE, css->NAME.name,
                  "writing 0x%02x to read-only "
                  "PCI config space address 0x%02lx\n",
                  val, addr);
#endif
            break;

      case C82371AB_USB_USBBA:      /* USB I/O Space Base Address (Bit  0-15) */
      case C82371AB_USB_USBBA + 1:
            if (addr == C82371AB_USB_USBBA) {
                  /* Clear bits hardwired to '0' */
                  val &= ~(SZ_USB_IOSPACE - 1); /* Signal 32 byte I/O space. */
                  /* Set bits hardwired to '1' */
                  val |= 1 << 0;                /* Signal I/O space. */
            }
            pci_setconfigb(css->NAME.config_space, addr, val);
            break;

      case C82371AB_USB_USBBA + 2:  /* USB I/O Space Base Address (Bit 16-31) */
      case C82371AB_USB_USBBA + 3:
            /* All bits are hardwired to 0. */
            /* Nothing to do. */
            break;

      case PCI_INTERRUPT_LINE:      /* Interrupt Line */
            pci_setconfigb(css->NAME.config_space, addr, val);
            break;

      case C82371AB_USB_LEGSUP:     /* Legacy Support (Bit 0-7) */
            /* Bit 6 is RO */
            old_val = pci_getconfigb(css->NAME.config_space, addr);
            val &= ~(1 << 6);
            val |= old_val & (1 << 6);

            pci_setconfigb(css->NAME.config_space, addr, val);
            break;

      case C82371AB_USB_LEGSUP + 1: /* Legacy Support (Bit 8-15) */
            old_val = pci_getconfigb(css->NAME.config_space, addr);
            /* Bit 8-11 and 15 is R/WC */
            old_val &= ~(val & (
                          (1<<(8-8))
                        | (1<<(9-8))
                        | (1<<(10-8))
                        | (1<<(11-8))
                        | (1<<(15-8))
                        ));

            /* Bit 13 is R/W */\
            old_val &= ~(1 << (13 - 8));
            old_val |= val & (1 << (13 - 8));

            /* Bit 12 is RO, Bit 14 is reserved, nothing more to do */

            val = old_val;

            pci_setconfigb(css->NAME.config_space, addr, val);
            break;

      case C82371AB_USB_MISCSUP:    /* Miscellaneous Support */
            /* FIXME sihoschi: make sure only byte accesses get here */
            /* Some bits are hardwired to '0'. */
            val &= (1 << 4);

            pci_setconfigb(css->NAME.config_space, addr, val);
            break;

      default:
            if (addr < 0x40) {
                  /*
                   * Writing to reserved config space registers
                   * *below* 0x40 is allowed. Value written is
                   * discarded.
                   */
                  /* Nothing to do... */

            } else {
                  /*
                   * Writing to reserved config space registers
                   * *above* 0x3f is *not* allowed. Programming error.
                   */
#if USB_DEBUGMASK & USB_DEBUG_WARNINGS
                  faum_log(FAUM_LOG_WARNING, USB_LOG_TYPE,
                        css->NAME.name,
                        "writing 0x%02x to "
                        "PCI config space address 0x%02lx\n",
                        val, addr);
#endif
            }
            break;
      };
}

static void
NAME_(cread)(struct cpssp *css, uint8_t addr, unsigned int bsel, uint32_t *valp)
{
      uint8_t val;

      assert(/* 0x00 <= addr && addr <= 0xff && */ (addr & 0x3) == 0);
      assert(0x1 <= bsel && bsel <= 0xf);

      *valp = 0x00000000;
      if ((bsel >> 0) & 1) {
            val = pci_getconfigb(css->NAME.config_space, addr + 0);
            *valp |= (uint32_t) val << 0;
      }
      if ((bsel >> 1) & 1) {
            val = pci_getconfigb(css->NAME.config_space, addr + 1);
            *valp |= (uint32_t) val << 8;
      }
      if ((bsel >> 2) & 1) {
            val = pci_getconfigb(css->NAME.config_space, addr + 2);
            *valp |= (uint32_t) val << 16;
      }
      if ((bsel >> 3) & 1) {
            val = pci_getconfigb(css->NAME.config_space, addr + 3);
            *valp |= (uint32_t) val << 24;
      }

#if USB_DEBUGMASK & USB_DEBUG_CONFSPACE
      faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
            "%s: addr=0x%02lx val=0x%08lx\n",
            __FUNCTION__, (unsigned long) addr, (unsigned long) *valp);
#endif
}

static void
NAME_(cwrite)(struct cpssp *css, uint8_t addr, unsigned int bsel, uint32_t val)
{
#if USB_DEBUGMASK & USB_DEBUG_CONFSPACE
      faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
            "%s: addr=0x%02lx val=0x%08lx\n",
            __FUNCTION__, (unsigned long) addr, (unsigned long) val);
#endif

      assert(/* 0x00 <= addr && addr <= 0xff && */ (addr & 0x3) == 0);
      assert(0x1 <= bsel && bsel <= 0xf);

      if ((bsel >> 0) & 1) {
            NAME_(_cwriteb)(css, (val >>  0) & 0xff, addr + 0);
      }
      if ((bsel >> 1) & 1) {
            NAME_(_cwriteb)(css, (val >>  8) & 0xff, addr + 1);
      }
      if ((bsel >> 2) & 1) {
            NAME_(_cwriteb)(css, (val >> 16) & 0xff, addr + 2);
      }
      if ((bsel >> 3) & 1) {
            NAME_(_cwriteb)(css, (val >> 24) & 0xff, addr + 3);
      }
}

/*----------------------------Auxiliary Functions-----------------------------*/

/* set I/O registers accoording to device attached or not */
static void
NAME_(connect_device)(struct cpssp *css, int usb_portnr)
{
      int current_connect_status = css->NAME.portsc[usb_portnr] & PORTSC_CURRENT_CONNECT_STATUS;
      char signal_global_resume = 0;

      if (css->NAME.portstate[usb_portnr].speed != USB_SPEED_UNCONNECTED) {
            if (! current_connect_status) {
                  css->NAME.portsc[usb_portnr] |=
                          PORTSC_CONNECT_STATUS_CHANGE
                        | PORTSC_CURRENT_CONNECT_STATUS;
                  signal_global_resume = 1;
            }

            if (css->NAME.portstate[usb_portnr].speed == USB_SPEED_FULL) { /* full speed device */
                  /* cf. USB Spec. 1.1, chapter 7.1.5, "Device Speed Identification"  */
                  css->NAME.portsc[usb_portnr] |=
                          PORTSC_LINE_STATUS_DPLUS;
                  css->NAME.portsc[usb_portnr] &=
                        ~(PORTSC_LINE_STATUS_DMINUS
                        | PORTSC_LOWSPEED_DEVICE_ATTACHED);
            } else { /* low speed device */
                  css->NAME.portsc[usb_portnr] |=
                          PORTSC_LOWSPEED_DEVICE_ATTACHED
                        | PORTSC_LINE_STATUS_DMINUS;
                  css->NAME.portsc[usb_portnr] &=
                        ~PORTSC_LINE_STATUS_DPLUS;
            }
      } else { /* no device attached */
            if (current_connect_status) {
                  css->NAME.portsc[usb_portnr] |=
                        PORTSC_CONNECT_STATUS_CHANGE;
                  signal_global_resume = 1;
            }
            css->NAME.portsc[usb_portnr] &=
                  ~(PORTSC_LOWSPEED_DEVICE_ATTACHED
                  | PORTSC_LINE_STATUS_DMINUS
                  | PORTSC_LINE_STATUS_DPLUS
                  | PORTSC_PORT_ENABLE_DISABLE_CHANGE
                  | PORTSC_PORT_ENABLE
                  | PORTSC_CURRENT_CONNECT_STATUS);
      }

      /* if Resume Interrupt Enabled and in Global Suspend Mode
       * and bits 1, 3 or 6 of PORTSC were set, signal interrupt */
      if (css->NAME.usbcmd & USBCMD_ENTER_GLOBAL_SUSPEND
       && signal_global_resume) {
            css->NAME.usbcmd |= USBCMD_FORCE_GLOBAL_RESUME;
            css->NAME.usbsts |= USBSTS_RESUME_DETECT;

            if (css->NAME.usbintr & USBINTR_RIE) {
                  NAME_(set_irq)(css, 1);
            }
      }
}

/* send a USB token packet over CIM */
static void
NAME_(send_usb_token)(
      struct cpssp *css,
      unsigned char pid,
      unsigned char addr,
      unsigned char endp
)
{
      int usb_portnr;

#if USB_DEBUGMASK & USB_DEBUG_DUMP_PACKETS
      faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
            "%s: sending %s packet to %02d:%02d\n",
            __FUNCTION__, cim_usb_debug_pid2str(pid), addr, endp);
#endif

      for (usb_portnr = 0; usb_portnr < USB_NUM_PORTS; usb_portnr++) {
            /* port disabled or in selective suspend mode? */
            if (! (css->NAME.portsc[usb_portnr] & PORTSC_PORT_ENABLE)
             || (css->NAME.portsc[usb_portnr] & PORTSC_PORT_ENABLE
              && css->NAME.portsc[usb_portnr] & PORTSC_SUSPEND)) {
                  continue;
            }

            if (usb_portnr == 0) {
                  usb0_send_token(css, pid, addr, endp);
            } else { assert(usb_portnr == 1);
                  usb1_send_token(css, pid, addr, endp);
            }
      }
}

/* send a USB start-of-frame packet over CIM */
static __attribute__ ((unused)) void
NAME_(send_usb_sof)(
      struct cpssp *css,
      unsigned short frame_num
)
{
      int usb_portnr;

#if USB_DEBUGMASK & USB_DEBUG_DUMP_PACKETS
      faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
            "%s: sending %s packet (frame %04d)\n",
            __FUNCTION__, cim_usb_debug_pid2str(USB_PID_SOF), frame_num);
#endif

      for (usb_portnr = 0; usb_portnr < USB_NUM_PORTS; usb_portnr++) {
            /* port disabled or in selective suspend mode? */
            if (!(css->NAME.portsc[usb_portnr] & PORTSC_PORT_ENABLE)
             || (css->NAME.portsc[usb_portnr] & PORTSC_PORT_ENABLE
              && css->NAME.portsc[usb_portnr] & PORTSC_SUSPEND)) {
                  continue;
            }

            if (usb_portnr == 0) {
                  usb0_send_sof(css, frame_num);
            } else { assert(usb_portnr == 1);
                  usb1_send_sof(css, frame_num);
            }
      }
}

/* send a USB data packet over CIM */
static void
NAME_(send_usb_data)(
      struct cpssp *css,
      unsigned char pid,
      unsigned char *data,
      unsigned int length
)
{
      int usb_portnr;

#if USB_DEBUGMASK & USB_DEBUG_DUMP_PACKETS
      int i;

      faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
            "%s: sending %s packet (length %d)",
            __FUNCTION__, cim_usb_debug_pid2str(pid), length);

      if (0 < length) {
            faum_cont(FAUM_LOG_DEBUG, " ( ");

            for (i = 0; i < length && i < 8; i++) {
                  faum_cont(FAUM_LOG_DEBUG, "%02X ", data[i]);
            }

            if (i != length) {
                  faum_cont(FAUM_LOG_DEBUG, "... ");
            }

            faum_cont(FAUM_LOG_DEBUG, ")");
      }

      faum_cont(FAUM_LOG_DEBUG, "\n");
#endif

      for (usb_portnr = 0; usb_portnr < USB_NUM_PORTS; usb_portnr++) {
            /* port disabled or in selective suspend mode? */
            if (!(css->NAME.portsc[usb_portnr] & PORTSC_PORT_ENABLE)
             || (css->NAME.portsc[usb_portnr] & PORTSC_PORT_ENABLE
              && css->NAME.portsc[usb_portnr] & PORTSC_SUSPEND)) {
                  continue;
            }

            if (usb_portnr == 0) {
                  usb0_send_data(css, pid, data, length, 0);
            } else { assert(usb_portnr == 1);
                  usb1_send_data(css, pid, data, length, 0);
            }
      }
}

/* send a USB handshake packet over CIM */
static void
NAME_(send_usb_handshake)(struct cpssp *css, unsigned char pid)
{
      int usb_portnr;

#if USB_DEBUGMASK & USB_DEBUG_DUMP_PACKETS
      faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
            "%s: sending %s packet\n",
            __FUNCTION__, cim_usb_debug_pid2str(pid));
#endif

      for (usb_portnr = 0; usb_portnr < USB_NUM_PORTS; usb_portnr++) {
            /* port disabled or in selective suspend mode? */
            if (!(css->NAME.portsc[usb_portnr] & PORTSC_PORT_ENABLE)
             || (css->NAME.portsc[usb_portnr] & PORTSC_PORT_ENABLE
              && css->NAME.portsc[usb_portnr] & PORTSC_SUSPEND)) {
                  continue;
            }

            if (usb_portnr == 0) {
                  usb0_send_handshake(css, pid);
            } else { assert(usb_portnr == 1);
                  usb1_send_handshake(css, pid);
            }
      }
}

/*----------------------In-memory data structure related----------------------*/

/* some forward declarations */
static void
NAME_(work_frame)(struct cpssp *css);
static void
NAME_(finish_frame)(struct cpssp *css);

static void
NAME_(read_td)(struct cpssp *css)
{
      uint32_t addr = css->NAME.tdp;

      /* fetch TD from memory (4 DWORDs) */
      usb_mem_read(css, addr +  0, 0xf, &css->NAME.td.td[0]);
      usb_mem_read(css, addr +  4, 0xf, &css->NAME.td.td[1]);
      usb_mem_read(css, addr +  8, 0xf, &css->NAME.td.td[2]);
      usb_mem_read(css, addr + 12, 0xf, &css->NAME.td.td[3]);

      css->NAME.td.tdlp = css->NAME.td.td[0] & USB_MEM_TD_0_LP;
      css->NAME.td.td_vf = css->NAME.td.td[0] & USB_MEM_TD_0_VF;
      css->NAME.td.td_q = css->NAME.td.td[0] & USB_MEM_TD_0_QH;
      css->NAME.td.td_t = css->NAME.td.td[0] & USB_MEM_TD_0_TERMINATE;

#if USB_DEBUGMASK & USB_DEBUG_WARNINGS
      /* check reserved bits */
      if (css->NAME.td.td[0] & USB_MEM_TD_0_RESERVED) {
            faum_log(FAUM_LOG_WARNING, USB_LOG_TYPE, css->NAME.name,
                        "%s: reserved bits in TD[0] set: %08lX\n",
                        __FUNCTION__,
                        (unsigned long) (css->NAME.td.td[0] & USB_MEM_TD_0_RESERVED));
      }
      if (css->NAME.td.td[1] & USB_MEM_TD_1_RESERVED) {
            faum_log(FAUM_LOG_WARNING, USB_LOG_TYPE, css->NAME.name,
                        "%s: reserved bits in TD[1] set: %08lX\n",
                        __FUNCTION__,
                        css->NAME.td.td[1] & USB_MEM_TD_1_RESERVED);
      }
#endif

      /* TD inactive? */
      if (!(css->NAME.td.td[1] & USB_MEM_TD_1_STATUS_ACTIVE)) {
            return;
      }

#if USB_DEBUGMASK & USB_DEBUG_WARNINGS
      if (css->NAME.td.td[2] & USB_MEM_TD_2_RESERVED) {
            faum_log(FAUM_LOG_WARNING, USB_LOG_TYPE, css->NAME.name,
                  "%s: reserved bits in TD[2] set: %08lX\n",
                  __FUNCTION__,
                  (unsigned long) (css->NAME.td.td[2] & USB_MEM_TD_2_RESERVED));
      }
#endif

      css->NAME.td.c_err = (css->NAME.td.td[1] & USB_MEM_TD_1_C_ERR)
                  >> USB_MEM_TD_1_C_ERR_SHIFT;
      css->NAME.td.actlen = (css->NAME.td.td[1] + 1) & USB_MEM_TD_1_ACTLEN;
      css->NAME.td.maxlen = (((css->NAME.td.td[2] & USB_MEM_TD_2_MAXLEN)
            >> USB_MEM_TD_2_MAXLEN_SHIFT) + 1) & USB_MEM_TD_2_MAXLEN_WRAP;

      css->NAME.td.endpt = (css->NAME.td.td[2] & USB_MEM_TD_2_ENDPT)
            >> USB_MEM_TD_2_ENDPT_SHIFT;
      css->NAME.td.addr = (css->NAME.td.td[2] & USB_MEM_TD_2_DEV_ADDR)
            >> USB_MEM_TD_2_DEV_ADDR_SHIFT;
      css->NAME.td.pid = css->NAME.td.td[2] & USB_MEM_TD_2_PID;
}

static void
NAME_(read_qh)(struct cpssp *css)
{
      int qh_nr;

#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
      faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
            "%s: reading QH @%08lX (#%lu in this frame)\n",
            __FUNCTION__, (unsigned long) css->NAME.qhp,
            css->NAME.qh_count);
#endif

      /*
       * QH to be read is at address css->NAME.qhp
       * Did we already visit it in this frame?
       */
      for (qh_nr = 0; qh_nr < css->NAME.qh_count; qh_nr++) {
            if (css->NAME.visited_qhs[qh_nr] != css->NAME.qhp) {
                  continue;
            }
            
            /*
             * We have already visited this QH in this frame, loop detected;
             * finish frame.
             */
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
            faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
                  "%s: QH has already been visited this frame (was QH #%d), aborting list traversal\n",
                  __FUNCTION__, qh_nr);
#endif
            NAME_(finish_frame)(css);
            return;
      }

      /* Store QH address for loop detection. */
      if (css->NAME.qh_count < USB_NUM_VISITED_QHS) {
            css->NAME.visited_qhs[css->NAME.qh_count] = css->NAME.qhp;
      }

      css->NAME.qh_count++;

      usb_mem_read(css, css->NAME.qhp + 0, 0xf, &css->NAME.qh.qhlp);
      usb_mem_read(css, css->NAME.qhp + 4, 0xf, &css->NAME.qh.qelp);

#if USB_DEBUGMASK & USB_DEBUG_WARNINGS
      if (css->NAME.qh.qhlp & USB_MEM_QH_0_RESERVED) {
            faum_log(FAUM_LOG_WARNING, USB_LOG_TYPE, css->NAME.name,
                  "%s: reserved bits in QHLP set\n", __FUNCTION__);
      }
#endif
      css->NAME.qh.qh_q = css->NAME.qh.qhlp & USB_MEM_QH_0_QH;
      css->NAME.qh.qh_t = css->NAME.qh.qhlp & USB_MEM_QH_0_TERMINATE;
      css->NAME.qh.qhlp &= USB_MEM_QH_0_QHLP;

#if USB_DEBUGMASK & USB_DEBUG_WARNINGS
      if (css->NAME.qh.qelp & USB_MEM_QH_1_RESERVED) {
            faum_log(FAUM_LOG_WARNING, USB_LOG_TYPE, css->NAME.name,
                  "%s: reserved bits in QELP set\n", __FUNCTION__);
      }
#endif
      css->NAME.qh.qe_vf = css->NAME.qh.qelp & USB_MEM_QH_1_VF;
      css->NAME.qh.qe_q = css->NAME.qh.qelp & USB_MEM_QH_1_QH;
      css->NAME.qh.qe_t = css->NAME.qh.qelp & USB_MEM_QH_1_TERMINATE;
      css->NAME.qh.qelp &= USB_MEM_QH_1_QELP;

      css->NAME.q_context = 1;

      /*
       * make all further code independent of whether we are executing the
       * first or any subsequent TD in this queue; needed for executing
       * multiple transactions at once
       */
      css->NAME.td.td_vf = css->NAME.qh.qe_vf;
      css->NAME.td.td_q = css->NAME.qh.qe_q;
      css->NAME.td.td_t = css->NAME.qh.qe_t;
      css->NAME.td.tdlp = css->NAME.qh.qelp;
}

static void
NAME_(advance_queue)(struct cpssp *css, int advance)
{
      assert(css->NAME.tds_sent == 0);

      if (css->NAME.q_context) {
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
            faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
                  "%s: in a queue context (QH @%08lX): ",
                  __FUNCTION__, (unsigned long) css->NAME.qhp);
#endif
            if (advance == USB_QUEUE_ADVANCE) {
                  /* tdlp -> qh.qelp (in memory, too) */
                  css->NAME.qh.qelp = css->NAME.td.tdlp;
                  css->NAME.qh.qe_vf = css->NAME.td.td_vf;
                  css->NAME.qh.qe_q = css->NAME.td.td_q;
                  css->NAME.qh.qe_t = css->NAME.td.td_t;

                  usb_mem_write(css, css->NAME.qhp + 4,
                        0xf, css->NAME.td.td[0]);

#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
                  faum_cont(FAUM_LOG_DEBUG, "queue advanced; ");
#endif
            }

            /*
             * We are ignoring td_vf completely here! FIXME
             */
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
            faum_cont(FAUM_LOG_DEBUG, "all necessary answers here, leaving queue\n");
#endif
                  
            /* go to the right */
            if (css->NAME.qh.qh_t) {
                  /* nothing comes next */
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
                  faum_cont(FAUM_LOG_DEBUG, "nothing to the right\n");
#endif
                  /* finish frame */
                  NAME_(finish_frame)(css);

            } else if (css->NAME.qh.qh_q) {
                  /* QH comes next */
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
                  faum_cont(FAUM_LOG_DEBUG, "QH @%08lX to the right\n",
                        (unsigned long) css->NAME.qh.qhlp);
#endif
                  css->NAME.qhp = css->NAME.qh.qhlp;
                  NAME_(read_qh)(css);

            } else {
                  /* TD comes next */
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
                  faum_cont(FAUM_LOG_DEBUG, "TD @%08lX to the right, leaving queue context\n",
                        (unsigned long) css->NAME.qh.qhlp);
#endif
                  css->NAME.td.tdlp = css->NAME.qh.qhlp;
                  /* this is missing in the specs! */
                  css->NAME.q_context = 0;
            }

      } else { /* not a queue context */
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
            faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
                  "%s: not in a queue context: ",
                  __FUNCTION__);
#endif
            if (css->NAME.td.td_t) {
                  /* nothing comes next */
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
                  faum_cont(FAUM_LOG_DEBUG, "nothing next\n");
#endif
                  /* finish frame */
                  NAME_(finish_frame)(css);

            } else if (css->NAME.td.td_q) {
                  /* QH is next */
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
                  faum_cont(FAUM_LOG_DEBUG, "QH @%08lX next\n",
                        (unsigned long) css->NAME.td.tdlp);
#endif
                  css->NAME.qhp = css->NAME.td.tdlp;
                  NAME_(read_qh)(css);

            } else {
                  /* TD is next */
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
                  faum_cont(FAUM_LOG_DEBUG, "TD @%08lX next\n",
                        (unsigned long) css->NAME.td.tdlp);
#endif
            }
      }
}

/* handle packet timeouts that may occur on device disconnection */
static void
NAME_(packet_timeout)(struct cpssp *css)
{
      uint32_t *tdctrl;
      
#if USB_DEBUGMASK & USB_DEBUG_TIMEOUTS
      faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
            "%s: packet timeout counter exceeds maximum, signaling timeout\n",
            __FUNCTION__);
#endif
      
      tdctrl = &css->NAME.td.td[1];

      /*
       * - signal a timeout error in the TD
       * - mark TD inactive
       * - signal STALLED
       * - set error count to zero (we do not retry even if error counter was >1)
       */
      *tdctrl &= ~(USB_MEM_TD_1_STATUS | USB_MEM_TD_1_C_ERR);
      *tdctrl |= USB_MEM_TD_1_STATUS_STALLED | USB_MEM_TD_1_STATUS_CRC_TIMEOUT_ERROR;

      /* write TD control and status back to memory */
      usb_mem_write(css, css->NAME.tdp + 4, 0xf, *tdctrl);

      /* generate interrupt if ordered to do so */
      css->NAME.timeout_interrupt = 1;

      /* we are not waiting for an answer anymore */
      css->NAME.tds_sent = 0;

      /* do not advance the current queue */
      NAME_(advance_queue)(css, USB_QUEUE_NO_ADVANCE);
}

/* handle incoming USB linestate packet */
static void
NAME_(handle_packet_linestate)(
      struct cpssp *css,
      int usb_portnr,
      int usb_speed
)
{
      css->NAME.portstate[usb_portnr].speed = usb_speed;
      NAME_(connect_device)(css, usb_portnr);
}

/* handle incoming USB data packet */
static void
NAME_(handle_packet_usb_data)(
      struct cpssp *css,
      int usb_portnr,
      unsigned char pid,
      unsigned int length,    /* not contained in real packets */
      unsigned char *data,
      unsigned short crc16
)
{
#if USB_DEBUGMASK & USB_DEBUG_DUMP_PACKETS
      int j;
#endif
      uint32_t *tdctrl;
      int advance_queue;

      /* only process USB token packets if powered and port enabled */
      if (! css->state_power
       || ! (css->NAME.portsc[usb_portnr] & PORTSC_PORT_ENABLE)) {
            faum_log(FAUM_LOG_WARNING, USB_LOG_TYPE,
                  css->NAME.name,
                  "ignoring incoming USB packets on port %d -- I am not powered, or port is disabled\n",
                  usb_portnr);
            return;
      }

      tdctrl = &css->NAME.td.td[1];

#if USB_DEBUGMASK & USB_DEBUG_DUMP_PACKETS
      faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
            "%s: received %s packet (length %d) on port %d",
            __FUNCTION__, cim_usb_debug_pid2str(pid), length, usb_portnr);

      if (0 < length) {
            faum_cont(FAUM_LOG_DEBUG, " ( ");

            for (j = 0; j < length && j < 8; j++) {
                  faum_cont(FAUM_LOG_DEBUG, "%02X ", data[j]);
            }

            if (j != length) {
                  faum_cont(FAUM_LOG_DEBUG, "... ");
            }

            faum_cont(FAUM_LOG_DEBUG, ")");
      }

      faum_cont(FAUM_LOG_DEBUG, "\n");
#endif

      if (css->NAME.td.pid != USB_PID_IN) {
#if USB_DEBUGMASK & USB_DEBUG_WARNINGS
            faum_log(FAUM_LOG_WARNING, USB_LOG_TYPE, css->NAME.name,
                  "%s: %s packet received on port %d although TD PID is %s\n",
                  __FUNCTION__, cim_usb_debug_pid2str(pid), usb_portnr,
                  cim_usb_debug_pid2str(css->NAME.td.pid));
#endif
            return;
      }

      /* c_err: must only be decremented for errors we do not simulate */
      /* data toggle correct? */
      if (((css->NAME.td.td[2] & USB_MEM_TD_2_D)
         && pid != USB_PID_DATA1)
       || (!(css->NAME.td.td[2] & USB_MEM_TD_2_D)
         && pid != USB_PID_DATA0)) {
            /*
             * Data toggle incorrect.
             */
#if USB_DEBUGMASK & USB_DEBUG_WARNINGS
            faum_log(FAUM_LOG_WARNING, USB_LOG_TYPE, css->NAME.name,
                  "%s: %s packet received on port %d although %s expected\n",
                  __FUNCTION__, cim_usb_debug_pid2str(pid),
                  usb_portnr, (css->NAME.td.td[2] & USB_MEM_TD_2_D) ?
                              cim_usb_debug_pid2str(USB_PID_DATA1) :
                              cim_usb_debug_pid2str(USB_PID_DATA0));
#endif
            advance_queue = USB_QUEUE_NO_ADVANCE;

            NAME_(send_usb_handshake)(css, USB_PID_ACK);

      } else {
            /*
             * Data toggle correct.
             */
            if (css->NAME.td.maxlen < length) {
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
                  faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
                        "%s: oversized %s packet received (%d bytes, maxlen = %d)\n",
                        __FUNCTION__, cim_usb_debug_pid2str(pid),
                        length, css->NAME.td.maxlen);
#endif
                  /* FIXME: is this the correct behaviour? */
                  /* this would be "babble" on real hardware */
                  length = css->NAME.td.maxlen;
            }

            /* short packet detected? */
            if (*tdctrl & USB_MEM_TD_1_SPD
             && css->NAME.q_context
             && length < css->NAME.td.maxlen) {
                  css->NAME.spd = 1;
                  advance_queue = USB_QUEUE_NO_ADVANCE;
            } else {
                  advance_queue = USB_QUEUE_ADVANCE;
            }

            /* IOC? */
            if (*tdctrl & USB_MEM_TD_1_IOC) {
                  css->NAME.ioc = 1;
            }

            /* put data in memory */
            if (0 < length) {
                  uint32_t addr;
                  unsigned int i;

                  addr = css->NAME.td.td[3];
                  i = 0;
                  while (i < length) {
                        unsigned int count;
                        unsigned int bs;
                        uint32_t val;

                        count = length - i;
                        if (4 < (addr & 3) + count) {
                              count = 4 - (addr & 3);
                        }
                        bs = ((1 << count) - 1) << (addr & 3);

                        val = 0;
                        if ((bs >> 0) & 1) {
                              val |= data[i++] << 0;
                        }
                        if ((bs >> 1) & 1) {
                              val |= data[i++] << 8;
                        }
                        if ((bs >> 2) & 1) {
                              val |= data[i++] << 16;
                        }
                        if ((bs >> 3) & 1) {
                              val |= data[i++] << 24;
                        }

                        usb_mem_write(css, addr & ~3, bs, val);

                        addr += count;
                  }
            }

            *tdctrl &= ~(USB_MEM_TD_1_STATUS | USB_MEM_TD_1_ACTLEN);

            /*
             * store data length:
             * this also takes care of the special 0 encoding
             */
            *tdctrl |= (length - 1) & USB_MEM_TD_1_ACTLEN;

            /* write TD control and status back to memory */
            usb_mem_write(css, css->NAME.tdp + 4, 0xf, *tdctrl);

            NAME_(send_usb_handshake)(css, USB_PID_ACK);
      }
      
      /* one answer less pending */
      css->NAME.tds_sent--;
      assert(css->NAME.tds_sent == 0);

      NAME_(advance_queue)(css, advance_queue);

      NAME_(work_frame)(css);
}

/* handle incoming USB handshake packet */
static void
NAME_(handle_packet_usb_handshake)(
      struct cpssp *css,
      int usb_portnr,
      unsigned char pid
)
{
      uint32_t *tdctrl;

      int advance_queue;

      /* only process USB token packets if powered and port enabled */
      if (! css->state_power
       || ! (css->NAME.portsc[usb_portnr] & PORTSC_PORT_ENABLE)) {
            faum_log(FAUM_LOG_WARNING, USB_LOG_TYPE,
                  css->NAME.name,
                  "ignoring incoming USB packets on port %d -- I am not powered, or port is disabled\n",
                  usb_portnr);
            return;
      }

      tdctrl = &css->NAME.td.td[1];

#if USB_DEBUGMASK & USB_DEBUG_DUMP_PACKETS
      faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
            "%s: %s handshake packet received on port %d\n",
            __FUNCTION__, cim_usb_debug_pid2str(pid), usb_portnr);
#endif

      /* c_err: must only be decremented for errors we do not simulate */
      /* maybe timeout? */
      if (pid == USB_PID_ACK) {
            /* FIXME: correct behaviour? */

            advance_queue = USB_QUEUE_ADVANCE;
            *tdctrl &= ~(USB_MEM_TD_1_STATUS | USB_MEM_TD_1_ACTLEN);
            *tdctrl |= (css->NAME.td.maxlen - 1) & USB_MEM_TD_1_ACTLEN;

            if (*tdctrl & USB_MEM_TD_1_IOC) {
                  css->NAME.ioc = 1;
            }

      } else if (pid == USB_PID_NAK) {
            /* FIXME: correct behaviour? */

            advance_queue = USB_QUEUE_NO_ADVANCE;
            /* *tdctrl &= ~(USB_MEM_TD_1_STATUS | USB_MEM_TD_1_ACTLEN); */
            *tdctrl &= ~USB_MEM_TD_1_ACTLEN;
            *tdctrl |= USB_MEM_TD_1_STATUS_NAK_RECEIVED;

            /* if this was an IN transaction, we didn't receive anything */
            if (css->NAME.td.pid == USB_PID_IN) {
                  *tdctrl |= USB_MEM_TD_1_ACTLEN;           /* encoded 0 */
            } else {
                  *tdctrl |= (css->NAME.td.maxlen - 1) & USB_MEM_TD_1_ACTLEN;
            }

            /*
             * If a NAK handshake is received from a SETUP transaction, a
             * Time Out Error will also be reported.
             */
            if (css->NAME.td.pid == USB_PID_SETUP) {
                  *tdctrl |= USB_MEM_TD_1_STATUS_CRC_TIMEOUT_ERROR;
            }
      } else if (pid == USB_PID_STALL) {
            /* FIXME: correct behaviour? */

            advance_queue = USB_QUEUE_NO_ADVANCE;
            *tdctrl &= ~(USB_MEM_TD_1_STATUS | USB_MEM_TD_1_ACTLEN);
            *tdctrl |= USB_MEM_TD_1_STATUS_STALLED;

            /* if this was an IN transaction, we didn't receive anything */
            if (css->NAME.td.pid == USB_PID_IN) {
                  *tdctrl |= USB_MEM_TD_1_ACTLEN;           /* encoded 0 */
            } else {
                  *tdctrl |= (css->NAME.td.maxlen - 1) & USB_MEM_TD_1_ACTLEN;
            }

            /*
             * If a STALL handshake is received from a SETUP transaction, a
             * Time Out Error will also be reported.
             */
            if (css->NAME.td.pid == USB_PID_SETUP) {
                  *tdctrl |= USB_MEM_TD_1_STATUS_CRC_TIMEOUT_ERROR;
            }

            css->NAME.stalled_interrupt = 1;
      } else {
            /* there are no other handshake types, programming error! */
            faum_log(FAUM_LOG_ERROR, USB_LOG_TYPE, css->NAME.name,
                  "%s: ignoring unknown handshake!\n",
                  __FUNCTION__);
            return;
      }

      /* write TD control and status back to memory */
      usb_mem_write(css, css->NAME.tdp + 4, 0xf, *tdctrl);

      /* one answer less pending */
      css->NAME.tds_sent--;

      NAME_(advance_queue)(css, advance_queue);

      NAME_(work_frame)(css);
}

static void
NAME_(usb0_speed_set)(struct cpssp *css, int val)
{
      NAME_(handle_packet_linestate)(css, 0, val);
}

static void
NAME_(usb0_recv_token)(
      struct cpssp *css,
      int pid,
      int addr,
      int endp
)
{
      fixme();
}

static void
NAME_(usb0_recv_sof)(
      struct cpssp *css,
      int frame_num
)
{
      fixme();
}

static void
NAME_(usb0_recv_data)(
      struct cpssp *css,
      int pid,
      unsigned int length,
      uint8_t *data,
      uint16_t crc16
)
{
      NAME_(handle_packet_usb_data)(css, 0, pid, length, data, crc16);
}

static void
NAME_(usb0_recv_handshake)(struct cpssp *css, int pid)
{
      NAME_(handle_packet_usb_handshake)(css, 0, pid);
}

static void
NAME_(usb1_speed_set)(struct cpssp *css, int val)
{
      NAME_(handle_packet_linestate)(css, 1, val);
}

static void
NAME_(usb1_recv_token)(
      struct cpssp *css,
      int pid,
      int addr,
      int endp
)
{
      fixme();
}

static void
NAME_(usb1_recv_sof)(
      struct cpssp *css,
      int frame_num
)
{
      fixme();
}

static void
NAME_(usb1_recv_data)(
      struct cpssp *css,
      int pid,
      unsigned int length,
      uint8_t *data,
      uint16_t crc16
)
{
      NAME_(handle_packet_usb_data)(css, 1, pid, length, data, crc16);
}

static void
NAME_(usb1_recv_handshake)(struct cpssp *css, int pid)
{
      NAME_(handle_packet_usb_handshake)(css, 1, pid);
}

static void
NAME_(debug_dump_td)(struct cpssp *css)
{
#if USB_DEBUGMASK & USB_DEBUG_DUMP_TDS
      faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
            "%s: raw TD:\n%08lX\n%08lX\n%08lX\n%08lX\n",
            __FUNCTION__,
            (unsigned long) css->NAME.td.td[0],
            (unsigned long) css->NAME.td.td[1],
            (unsigned long) css->NAME.td.td[2],
            (unsigned long) css->NAME.td.td[3]);

      faum_cont(FAUM_LOG_DEBUG, "-----------------------------------------------------------\n");
      faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
            "%s: information from TD @%08lx:\n"
            "1) LP: %08lX %s %s %s\n"
            "2) %s C_ERR=%d %s %s %s ACTLEN=%d\n"
            "   STATUS: %s %s %s %s %s %s %s\n"
            "3) MAXLEN=%4d DATA=%d ENDPT=%2d ADDRESS=%2d PID=%02X (%s)\n"
            "4) BUFFER POINTER: %08lX\n",
            __FUNCTION__,
            (unsigned long) css->NAME.tdp,

            (unsigned long) css->NAME.td.tdlp,
            css->NAME.td.td_vf ? "VF" : "vf",
            css->NAME.td.td_q ? "Q" : "q",
            css->NAME.td.td_t ? "T" : "t",

            css->NAME.td.td[1] & USB_MEM_TD_1_SPD ? "SPD" : "spd",
            css->NAME.td.c_err,
            css->NAME.td.td[1] & USB_MEM_TD_1_LS ? "LS" : "ls",
            css->NAME.td.td[1] & USB_MEM_TD_1_IOS ? "IOS" : "ios",
            css->NAME.td.td[1] & USB_MEM_TD_1_IOC ? "IOC" : "ioc",
            css->NAME.td.actlen,

            css->NAME.td.td[1] & USB_MEM_TD_1_STATUS_ACTIVE ? "ACTIVE" : "active",
            css->NAME.td.td[1] & USB_MEM_TD_1_STATUS_STALLED ? "STALLED" : "stalled",
            css->NAME.td.td[1] & USB_MEM_TD_1_STATUS_DATA_BUFFER_ERROR ? "DATABUF" : "databuf",
            css->NAME.td.td[1] & USB_MEM_TD_1_STATUS_BABBLE_DETECTED ? "BABBLE" : "babble",
            css->NAME.td.td[1] & USB_MEM_TD_1_STATUS_NAK_RECEIVED ? "NAK" : "nak",
            css->NAME.td.td[1] & USB_MEM_TD_1_STATUS_CRC_TIMEOUT_ERROR ? "TIMEOUT" : "timeout",
            css->NAME.td.td[1] & USB_MEM_TD_1_STATUS_BITSTUFF_ERROR ? "BITSTUFF" : "bitstuff",

            css->NAME.td.maxlen,
            css->NAME.td.td[2] & USB_MEM_TD_2_D ? 1 : 0,
            css->NAME.td.endpt,
            css->NAME.td.addr,
            css->NAME.td.pid,
            cim_usb_debug_pid2str(css->NAME.td.pid),

            (unsigned long) css->NAME.td.td[3]);
      faum_cont(FAUM_LOG_DEBUG, "-----------------------------------------------------------\n");
#endif
}

static void
NAME_(execute_td)(struct cpssp *css)
{
      /* TD inactive? */
      if (!(css->NAME.td.td[1] & USB_MEM_TD_1_STATUS_ACTIVE)) {
            /* IOC set? */
            if (css->NAME.td.td[1] & USB_MEM_TD_1_IOC) {
                  css->NAME.ioc = 1;
            }

            NAME_(advance_queue)(css, USB_QUEUE_NO_ADVANCE);
            return;
      }

      if (USB_MEM_TD_2_MAXLEN_ILLEGAL <= css->NAME.td.maxlen) {
#if USB_DEBUGMASK & USB_DEBUG_WARNINGS
            faum_log(FAUM_LOG_WARNING, USB_LOG_TYPE, css->NAME.name,
                        "%s: maxlen is 0x%X >= 0x%X, signaling HC process error\n",
                        __FUNCTION__, css->NAME.td.maxlen, USB_MEM_TD_2_MAXLEN_ILLEGAL);
#endif
            css->NAME.hc_process_error_interrupt = 1;
            NAME_(finish_frame)(css);
            return;
      }

      /* check PID validity */
      if (css->NAME.td.pid != USB_PID_SETUP
       && css->NAME.td.pid != USB_PID_IN
       && css->NAME.td.pid != USB_PID_OUT) {
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
            faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
                  "%s: erroneous PID %02X found, signaling HC process error\n",
                  __FUNCTION__, css->NAME.td.pid);
#endif
            css->NAME.hc_process_error_interrupt = 1;
            NAME_(finish_frame)(css);
            return;
      }

      NAME_(debug_dump_td)(css);

      /* now we are sure we'll really execute this TD */

      css->NAME.tds_sent++;

      /*
       * In IN transfers the function is the sender, in
       * OUT and SETUP transfers the host sends.
       */
      if (css->NAME.td.pid == USB_PID_SETUP
       || css->NAME.td.pid == USB_PID_OUT) {
            if (0 < css->NAME.td.maxlen) {
                  /* fetch data from memory */
                  uint32_t addr;
                  unsigned int i;

                  addr = css->NAME.td.td[3];
                  i = 0;
                  while (i < css->NAME.td.maxlen) {
                        unsigned int count;
                        unsigned int bs;
                        uint32_t val;

                        count = css->NAME.td.maxlen - i;
                        if (4 < (addr & 3) + count) {
                              count = 4 - (addr & 3);
                        }
                        bs = ((1 << count) - 1) << (addr & 3);

                        usb_mem_read(css, addr & ~3, bs, &val);

                        if ((bs >> 0) & 1) {
                              css->NAME.sendbuffer[i++] = val >> 0;
                        }
                        if ((bs >> 1) & 1) {
                              css->NAME.sendbuffer[i++] = val >> 8;
                        }
                        if ((bs >> 2) & 1) {
                              css->NAME.sendbuffer[i++] = val >> 16;
                        }
                        if ((bs >> 3) & 1) {
                              css->NAME.sendbuffer[i++] = val >> 24;
                        }

                        addr += count;
                  }
            }

            /* Send Token */
            NAME_(send_usb_token)(css, css->NAME.td.pid,
                        css->NAME.td.addr, css->NAME.td.endpt);

            /* Send Data Packet */
            NAME_(send_usb_data)(css,
                        css->NAME.td.td[2] & USB_MEM_TD_2_D ?
                                    USB_PID_DATA1 : USB_PID_DATA0,
                        css->NAME.sendbuffer,
                        css->NAME.td.maxlen);
      } else {
            /* Send Token */
            NAME_(send_usb_token)(css, css->NAME.td.pid,
                        css->NAME.td.addr, css->NAME.td.endpt);
      }

      /* isochroneous TD and SETUP/OUT? */
      if (css->NAME.td.td[1] & USB_MEM_TD_1_IOS
       && (css->NAME.td.pid == USB_PID_SETUP
        || css->NAME.td.pid == USB_PID_OUT)) {
            /* mark inactive, do not wait for an answer */
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
            faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
                  "%s: TD with IOS set!\n", __FUNCTION__);
#endif
            NAME_(handle_packet_usb_handshake)(css, 0, USB_PID_ACK);
      }
}

static void
NAME_(start_frame)(struct cpssp *css)
{
      uint32_t flp;

#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
      faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
            "%s: starting a new frame (frnum %04d)\n",
            __FUNCTION__, css->NAME.frnum);
#endif

      css->NAME.working = 1;
      css->NAME.timeout_interrupt =
            css->NAME.ioc =
            css->NAME.spd =
            css->NAME.babble_interrupt =
            css->NAME.stalled_interrupt =
            css->NAME.data_buffer_error_interrupt =
            css->NAME.bit_stuff_error_interrupt =
            css->NAME.hc_process_error_interrupt =
            css->NAME.host_system_error_interrupt = 0;

      css->NAME.qh_count = 0;

      /* TODO: issue SOF? */

      /* read frame list pointer from frame list */
      usb_mem_read(css, css->NAME.flbaseadd +
            ((css->NAME.frnum & FRNUM_FL_INDEX) << 2),
            0xf, &flp);

#if USB_DEBUGMASK & USB_DEBUG_WARNINGS
      if (flp & USB_MEM_FLP_RESERVED) {
            faum_log(FAUM_LOG_WARNING, USB_LOG_TYPE, css->NAME.name,
                  "%s: reserved bits in frame list entry set\n", __FUNCTION__);
      }
#endif

      /* initialize frame list traversal state */
      if (flp & USB_MEM_FLP_TERMINATE) {
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
            faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
                  "%s: frame list entry with T bit set\n", __FUNCTION__);
#endif
            NAME_(finish_frame)(css);
            return;
      } else if (flp & USB_MEM_FLP_QH) {  /* first structure is a QH */
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
            faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
                  "%s: first structure in frame is a QH\n", __FUNCTION__);
#endif
            css->NAME.qhp = flp & USB_MEM_FLP_FLP;
            NAME_(read_qh)(css);
      } else { /* first structure is a TD */
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
            faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
                  "%s: first structure in frame is a TD\n", __FUNCTION__);
#endif
            css->NAME.td.tdlp = flp & USB_MEM_FLP_FLP;
            css->NAME.q_context = 0;
      }

      /* we haven't done any communications up to now, so we're not waiting
       * for packets already */
      css->NAME.tds_sent = 0;

      NAME_(work_frame)(css);
}

static void
NAME_(work_frame)(struct cpssp *css)
{
      if (css->NAME.tds_sent) {
            /* Wait for answer first... */
            return;
      }
 
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
      faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
            "%s: entering loop\n",
            __FUNCTION__);
#endif

      do {
            /*
             * cf. UHCI Spec., Rev. 1.1, chapter 3.4.2 "Transfer Queueing",
             * "USB Schedule List Traversal Decision Table" (incomplete!)
             * and "USB Linked List Traversal State Diagram"
             */

            /* states #1, #2, #3 */
            if (!css->NAME.q_context) {
                  /*
                   * Not in a queue context.
                   */
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
                  faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
                        "%s: not in a queue context, executing next TD @%08lX\n",
                        __FUNCTION__, (unsigned long) css->NAME.td.tdlp);
#endif

                  /* execute TD pointed at by css->NAME.td.tdlp */
                  css->NAME.tdp = css->NAME.td.tdlp;
                  NAME_(read_td)(css);
                  NAME_(execute_td)(css);

            } else {
                  /*
                   * Queue context, states #4-#12
                   */
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
                  faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
                        "%s: in a queue context (QH @%08lX): ",
                        __FUNCTION__, (unsigned long) css->NAME.qhp);
#endif

                  /* states #4, #5, #6, #9, #11 */
                  if (!css->NAME.td.td_t
                   && !css->NAME.td.td_q) {
                        /* TD below */
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
                        faum_cont(FAUM_LOG_DEBUG, "TD @%08lX below\n",
                              (unsigned long) css->NAME.td.tdlp);
#endif
                        /* execute TD pointed at by css->NAME.td.tdlp */
                        css->NAME.tdp = css->NAME.td.tdlp;
                        NAME_(read_td)(css);
                        NAME_(execute_td)(css);
                        /* we are probably waiting for answers now */

                  /* state #8 */
                  } else if (!css->NAME.td.td_t
                        && css->NAME.td.td_q) {
                        /* QH below */
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
                        faum_cont(FAUM_LOG_DEBUG, "QH @%08lX below\n",
                              (unsigned long) css->NAME.qh.qelp);
#endif
                        css->NAME.qhp = css->NAME.qh.qelp;
                        NAME_(read_qh)(css);
                        /* we are not yet waiting for answers, loop! */

                  /* states #7, #10, #12 */
                  } else { /* td.td_t is set */
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
                        faum_cont(FAUM_LOG_DEBUG, "no (more) entries in this queue, ");
#endif

                        /* state #10 */
                        if (css->NAME.qh.qh_t) {
                              /* QH with nothing to the right */
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
                              faum_cont(FAUM_LOG_DEBUG, "nothing to the right\n");
#endif
                              /* end of frame */
                              NAME_(finish_frame)(css);
                              /* we are done for this frame, leaving the loop */

                        /* state #7 */
                        } else if (!css->NAME.qh.qh_q) {
                              /* QH with TD to the right */
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
                              faum_cont(FAUM_LOG_DEBUG, "TD @%08lX to the right\n",
                                    (unsigned long) css->NAME.qh.qhlp);
#endif
                              css->NAME.td.tdlp = css->NAME.qh.qhlp;
                              /* leaving queue context! specs are buggy here! */
                              css->NAME.q_context = 0;
                              /* we are not yet waiting for answers, loop! */

                        /* state #12 */
                        } else if (css->NAME.qh.qh_q) {
                              /* QH with QH to the right */
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
                              faum_cont(FAUM_LOG_DEBUG, "QH @%08lX to the right\n",
                                    (unsigned long) css->NAME.qh.qhlp);
#endif
                              css->NAME.qhp = css->NAME.qh.qhlp;
                              NAME_(read_qh)(css);
                              /* we are not yet waiting for answers, loop! */
                        }
                  }
            }

            if (css->NAME.tds_sent == 0
             && USB_MAX_QH_PER_FRAME <= css->NAME.qh_count) {
#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
                  /*
                   * If the simulation hits this point, two constants
                   * might need to be adjusted:
                   * - USB_MAX_QH_PER_FRAME might need to be increased in
                   *   case the in-memory QH chain gets too long (too
                   *   many devices attached, too many endpoints per
                   *   device)
                   * - USB_NUM_VISITED_QHS might need to be increased in
                   *   case the QH chain loops back to a QH too late in
                   *   the list (too many INT queues, too many low-speed
                   *   CTRL or BULK queues) (unlikely)
                   */
                  faum_log(FAUM_LOG_WARNING, USB_LOG_TYPE, css->NAME.name,
                        "%s: forcing frame termination, qh_count exceeded limit -- this is only a fallback mechanism!\n",
                        __FUNCTION__);
#endif
                  NAME_(finish_frame)(css);
            }
      } while (css->NAME.tds_sent == 0
            && css->NAME.working);

#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
      faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
            "%s: no more work this time\n",
            __FUNCTION__);
#endif
}

static void
NAME_(finish_frame)(struct cpssp *css)
{
      int do_interrupt = 0;

#if USB_DEBUGMASK & USB_DEBUG_FRAME_LIST
      faum_log(FAUM_LOG_DEBUG, USB_LOG_TYPE, css->NAME.name,
            "%s: finishing frame\n",
            __FUNCTION__);
#endif

      css->NAME.working = 0;
      assert(css->NAME.tds_sent == 0);

      /* generate interrupt? */
      /* cf. UHCI Spec. Rev. 1.1, chapter 4, "Interrupts" */
      if (css->NAME.timeout_interrupt) {
            if (css->NAME.usbintr & USBINTR_TIE) {
                  do_interrupt = 1;
            }
            css->NAME.usbsts |= USBSTS_ERROR_INTERRUPT;
      }
      if (css->NAME.ioc) {
            if (css->NAME.usbintr & USBINTR_IOC) {
                  do_interrupt = 1;
            }
            css->NAME.usbsts |= USBSTS_INTERRUPT;
      }
      if (css->NAME.spd) {
            if (css->NAME.usbintr & USBINTR_SPIE) {
                  do_interrupt = 1;
            }
            css->NAME.usbsts |= USBSTS_INTERRUPT;
      }
      /* TODO: resume received missing */
      if (css->NAME.babble_interrupt
       || css->NAME.stalled_interrupt
       || css->NAME.data_buffer_error_interrupt
       || css->NAME.bit_stuff_error_interrupt
       || css->NAME.hc_process_error_interrupt
       || css->NAME.host_system_error_interrupt) {
            do_interrupt = 1; /* non-maskable */
            if (css->NAME.babble_interrupt
             || css->NAME.stalled_interrupt
             || css->NAME.data_buffer_error_interrupt
             || css->NAME.bit_stuff_error_interrupt) {
                  css->NAME.usbsts |= USBSTS_ERROR_INTERRUPT;
            }
            if (css->NAME.hc_process_error_interrupt) {
                  css->NAME.usbsts |= USBSTS_HC_PROCESS_ERROR;
                  css->NAME.usbsts |= USBSTS_HC_HALTED;
                  css->NAME.usbcmd &= ~USBCMD_RUN_STOP;
            }
            if (css->NAME.host_system_error_interrupt) {
                  css->NAME.usbsts |= USBSTS_HC_HALTED;
                  css->NAME.usbcmd &= ~USBCMD_RUN_STOP;
            }
      }

      if (do_interrupt) {
            NAME_(set_irq)(css, 1);
      }

#if 1
      css->NAME.tsc_passed += css->NAME.tsc_step_busy;
#else
      css->NAME.tsc_passed += TIME_HZ / 10;
#endif
      css->NAME.frnum++;
      css->NAME.frnum &= FRNUM_FRNUM;

      /* re-schedule timer handler for next frame */
      css->NAME.timer_scheduled++;
      time_call_at(css->NAME.tsc_passed + css->NAME.tsc_step_busy,
                  NAME_(timer_event), css);
}

static void
NAME_(timer_event)(void *datap)
{
      struct cpssp *css = datap;

      /* not scheduled anymore */
      css->NAME.timer_scheduled--;

      if (! css->state_power) {           /* not powered */
            goto schedule_idle;
      }

      /* check BME (bus master enable), check schedule flag, check HCHalted */
      if (pci_getconfigl(css->NAME.config_space, PCI_COMMAND) & PCI_COMMAND_MASTER
       && css->NAME.usbcmd & USBCMD_RUN_STOP
       && !(css->NAME.usbsts & USBSTS_HC_HALTED)) {
            if (!css->NAME.working) {
                  /* start a new frame */
                  NAME_(start_frame)(css);
            } else { /* we are working on a frame */
                  NAME_(work_frame)(css);
            }
      } else { /* stopped or HCHalted */
            css->NAME.working = 0;
            goto schedule_idle;
      }

      /*
       * schedule in any case: external devices might not respond and we
       * won't be woken up anymore if we don't additionally schedule with
       * time again; NAME_(work_frame)() handles timeouts (which
       * may occur on device disconnection for instance).
       */

schedule_idle:
      if (! css->NAME.timer_scheduled) {
            css->NAME.timer_scheduled++;
            css->NAME.tsc_passed += css->NAME.tsc_step_idle;
            time_call_at(css->NAME.tsc_passed + css->NAME.tsc_step_idle,
                        NAME_(timer_event), css);
      }
}

static void
NAME_(init)(struct cpssp *css)
{
      /* schedule timer event handler */
      css->NAME.tsc_passed = time_virt();
      css->NAME.tsc_step_idle = TIME_HZ / USB_TIMER_FREQ_IDLE;
      css->NAME.tsc_step_busy = TIME_HZ / USB_TIMER_FREQ_BUSY;
      time_call_at(css->NAME.tsc_passed + css->NAME.tsc_step_idle,
                  NAME_(timer_event), css);
      css->NAME.timer_scheduled++;
}

/*
 * pci_interrupt_pin:
 *  PCI_INT_D if used within the i82371AB southbridge,
 *  PCI_INT_A if used in a PCI card.
 */
static void
NAME_(create)(struct cpssp *css, const char *name, int pci_interrupt_pin)
{
      strncpy(css->NAME.name, name, sizeof(css->NAME.name));
      /* force zero termination; strncpy does not take care of that */
      css->NAME.name[sizeof(css->NAME.name) - 1] = 0;

      css->NAME.timer_scheduled = 0;

      /* initialize PCI configuration space */
      memset(css->NAME.config_space, 0, sizeof(css->NAME.config_space));
      pci_setconfigw(css->NAME.config_space, PCI_VENDOR_ID, PCI_VENDOR_ID_INTEL);
      pci_setconfigw(css->NAME.config_space, PCI_DEVICE_ID, 0x7112);
      pci_setconfigw(css->NAME.config_space, PCI_COMMAND, 0x0000);
      pci_setconfigw(css->NAME.config_space, PCI_STATUS, 0x0280);
      /* FIXME? --sihoschi */
      pci_setconfigb(css->NAME.config_space, PCI_REVISION_ID, 0x0);

      pci_setconfigb(css->NAME.config_space, PCI_CLASS_PROG, 0x00);
      pci_setconfigw(css->NAME.config_space, PCI_CLASS_DEVICE, PCI_CLASS_SERIAL_USB);

      pci_setconfigl(css->NAME.config_space,
                  PCI_BASE_ADDRESS_4,
                  PCI_BASE_ADDRESS_SPACE_IO);

      pci_setconfigb(css->NAME.config_space, PCI_INTERRUPT_PIN, pci_interrupt_pin);
      /* Serial Bus Specification Release Number: Release 1.0 */
      pci_setconfigb(css->NAME.config_space, C82371AB_USB_SBRNUM, 0x10);
      pci_setconfigw(css->NAME.config_space, C82371AB_USB_LEGSUP, 0x2000);
}

static void
NAME_(destroy)(struct cpssp *css)
{
}

#endif /* BEHAVIOR */

Generated by  Doxygen 1.6.0   Back to index