Home | History | Annotate | Line # | Download | only in mips
dv-tx3904sio.c revision 1.1.1.3
      1 /*  This file is part of the program GDB, the GNU debugger.
      2 
      3     Copyright (C) 1998-2016 Free Software Foundation, Inc.
      4     Contributed by Cygnus Solutions.
      5 
      6     This program is free software; you can redistribute it and/or modify
      7     it under the terms of the GNU General Public License as published by
      8     the Free Software Foundation; either version 3 of the License, or
      9     (at your option) any later version.
     10 
     11     This program is distributed in the hope that it will be useful,
     12     but WITHOUT ANY WARRANTY; without even the implied warranty of
     13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14     GNU General Public License for more details.
     15 
     16     You should have received a copy of the GNU General Public License
     17     along with this program.  If not, see <http://www.gnu.org/licenses/>.
     18 
     19     */
     20 
     21 
     22 #include "sim-main.h"
     23 #include "hw-main.h"
     24 #include "dv-sockser.h"
     25 #include "sim-assert.h"
     26 
     27 
     28 /* DEVICE
     29 
     30 
     31    tx3904sio - tx3904 serial I/O
     32 
     33 
     34    DESCRIPTION
     35 
     36 
     37    Implements one tx3904 serial I/O controller described in the tx3904
     38    user guide.  Three instances are required for SIO0 and SIO1 within
     39    the tx3904, at different base addresses.
     40 
     41    Both internal and system clocks are synthesized as divided versions
     42    of the simulator clock.
     43 
     44    There is no support for:
     45     - CTS/RTS flow control
     46     - baud rate emulation - use infinite speed instead
     47     - general frame format - use 8N1
     48     - multi-controller system
     49     - DMA - use interrupt-driven or polled-I/O instead
     50 
     51 
     52    PROPERTIES
     53 
     54 
     55    reg <base> <length>
     56 
     57    Base of SIO control register bank.  <length> must equal 0x100.
     58    Register offsets:       0: SLCR: line control register
     59                            4: SLSR: line status register
     60                            8: SDICR: DMA/interrupt control register
     61                           12: SDISR: DMA/interrupt status register
     62                           16: SFCR: FIFO control register
     63 			  20: SBGR: baud rate control register
     64 			  32: transfer FIFO buffer
     65 			  48: transfer FIFO buffer
     66 
     67    backend {tcp | stdio}
     68 
     69    Use dv-sockser TCP-port backend or stdio for backend.  Default: stdio.
     70 
     71 
     72 
     73    PORTS
     74 
     75 
     76    int (output)
     77 
     78    Interrupt port.  An event is generated when a timer interrupt
     79    occurs.
     80 
     81 
     82    reset (input)
     83 
     84    Reset port.
     85 
     86    */
     87 
     88 
     89 
     90 /* static functions */
     91 
     92 struct tx3904sio_fifo;
     93 
     94 static void tx3904sio_tickle(struct hw*);
     95 static int tx3904sio_fifo_nonempty(struct hw*, struct tx3904sio_fifo*);
     96 static char tx3904sio_fifo_pop(struct hw*, struct tx3904sio_fifo*);
     97 static void tx3904sio_fifo_push(struct hw*, struct tx3904sio_fifo*, char);
     98 static void tx3904sio_fifo_reset(struct hw*, struct tx3904sio_fifo*);
     99 static void tx3904sio_poll(struct hw*, void* data);
    100 
    101 
    102 /* register numbers; each is one word long */
    103 enum
    104 {
    105   SLCR_REG = 0,
    106   SLSR_REG = 1,
    107   SDICR_REG = 2,
    108   SDISR_REG = 3,
    109   SFCR_REG = 4,
    110   SBGR_REG = 5,
    111   TFIFO_REG = 8,
    112   SFIFO_REG = 12,
    113 };
    114 
    115 
    116 
    117 /* port ID's */
    118 
    119 enum
    120  {
    121   RESET_PORT,
    122   INT_PORT,
    123 };
    124 
    125 
    126 static const struct hw_port_descriptor tx3904sio_ports[] =
    127 {
    128   { "int", INT_PORT, 0, output_port, },
    129   { "reset", RESET_PORT, 0, input_port, },
    130   { NULL, },
    131 };
    132 
    133 
    134 
    135 /* Generic FIFO */
    136 struct tx3904sio_fifo
    137 {
    138   int size, used;
    139   unsigned_1 *buffer;
    140 };
    141 
    142 
    143 
    144 /* The timer/counter register internal state.  Note that we store
    145    state using the control register images, in host endian order. */
    146 
    147 struct tx3904sio
    148 {
    149   address_word base_address; /* control register base */
    150   enum {sio_tcp, sio_stdio} backend; /* backend */
    151 
    152   struct tx3904sio_fifo rx_fifo, tx_fifo; /* FIFOs */
    153 
    154   unsigned_4 slcr;
    155 #define SLCR_WR_MASK        0xe17f0000U
    156 #define SLCR_SET_BYTE(c,o,b) ((c)->slcr = SLCR_WR_MASK & (((c)->slcr & ~LSMASK32((o)*8+7,(o)*8)) | ((b)<< (o)*8)))
    157   unsigned_4 slsr;
    158 #define SLSR_WR_MASK        0x00000000 /* UFER/UPER/UOER unimplemented */
    159   unsigned_4 sdicr;
    160 #define SDICR_WR_MASK       0x000f0000U
    161 #define SDICR_SET_BYTE(c,o,b) ((c)->sdicr = SDICR_WR_MASK & (((c)->sdicr & ~LSMASK32((o)*8+7,(o)*8)) | ((b)<< (o)*8)))
    162 #define SDICR_GET_SDMAE(c)  ((c)->sdicr & 0x00080000)
    163 #define SDICR_GET_ERIE(c)   ((c)->sdicr & 0x00040000)
    164 #define SDICR_GET_TDIE(c)   ((c)->sdicr & 0x00020000)
    165 #define SDICR_GET_RDIE(c)   ((c)->sdicr & 0x00010000)
    166   unsigned_4 sdisr;
    167 #define SDISR_WR_MASK       0x00070000U
    168 #define SDISR_SET_BYTE(c,o,b) ((c)->sdisr = SDISR_WR_MASK & (((c)->sdisr & ~LSMASK32((o)*8+7,(o)*8)) | ((b)<< (o)*8)))
    169 #define SDISR_CLEAR_FLAG_BYTE(c,o,b) ((c)->sdisr = SDISR_WR_MASK & (((c)->sdisr & ~LSMASK32((o)*8+7,(o)*8)) & ((b)<< (o)*8)))
    170 #define SDISR_GET_TDIS(c)   ((c)->sdisr & 0x00020000)
    171 #define SDISR_SET_TDIS(c)   ((c)->sdisr |= 0x00020000)
    172 #define SDISR_GET_RDIS(c)   ((c)->sdisr & 0x00010000)
    173 #define SDISR_SET_RDIS(c)   ((c)->sdisr |= 0x00010000)
    174   unsigned_4 sfcr;
    175 #define SFCR_WR_MASK       0x001f0000U
    176 #define SFCR_SET_BYTE(c,o,b) ((c)->sfcr = SFCR_WR_MASK & (((c)->sfcr & ~LSMASK32((o)*8+7,(o)*8)) | ((b)<< (o)*8)))
    177 #define SFCR_GET_TFRST(c)   ((c)->sfcr & 0x00040000)
    178 #define SFCR_GET_RFRST(c)   ((c)->sfcr & 0x00020000)
    179 #define SFCR_GET_FRSTE(c)   ((c)->sfcr & 0x00010000)
    180   unsigned_4 sbgr;
    181 #define SBGR_WR_MASK       0x03ff0000U
    182 #define SBGR_SET_BYTE(c,o,b) ((c)->sbgr = SBGR_WR_MASK & (((c)->sbgr & ~LSMASK32((o)*8+7,(o)*8)) | ((b)<< (o)*8)))
    183 
    184   /* Periodic I/O polling */
    185   struct hw_event* poll_event;
    186 };
    187 
    188 
    189 
    190 /* Finish off the partially created hw device.  Attach our local
    191    callbacks.  Wire up our port names etc */
    192 
    193 static hw_io_read_buffer_method tx3904sio_io_read_buffer;
    194 static hw_io_write_buffer_method tx3904sio_io_write_buffer;
    195 static hw_port_event_method tx3904sio_port_event;
    196 
    197 
    198 static void
    199 attach_tx3904sio_regs (struct hw *me,
    200 		      struct tx3904sio *controller)
    201 {
    202   unsigned_word attach_address;
    203   int attach_space;
    204   unsigned attach_size;
    205   reg_property_spec reg;
    206 
    207   if (hw_find_property (me, "reg") == NULL)
    208     hw_abort (me, "Missing \"reg\" property");
    209 
    210   if (!hw_find_reg_array_property (me, "reg", 0, &reg))
    211     hw_abort (me, "\"reg\" property must contain one addr/size entry");
    212 
    213   hw_unit_address_to_attach_address (hw_parent (me),
    214 				     &reg.address,
    215 				     &attach_space,
    216 				     &attach_address,
    217 				     me);
    218   hw_unit_size_to_attach_size (hw_parent (me),
    219 			       &reg.size,
    220 			       &attach_size, me);
    221 
    222   hw_attach_address (hw_parent (me), 0,
    223 		     attach_space, attach_address, attach_size,
    224 		     me);
    225 
    226   if(hw_find_property(me, "backend") != NULL)
    227     {
    228       const char* value = hw_find_string_property(me, "backend");
    229       if(! strcmp(value, "tcp"))
    230 	controller->backend = sio_tcp;
    231       else if(! strcmp(value, "stdio"))
    232 	controller->backend = sio_stdio;
    233       else
    234 	hw_abort(me, "illegal value for backend parameter `%s': use tcp or stdio", value);
    235     }
    236 
    237   controller->base_address = attach_address;
    238 }
    239 
    240 
    241 static void
    242 tx3904sio_finish (struct hw *me)
    243 {
    244   struct tx3904sio *controller;
    245 
    246   controller = HW_ZALLOC (me, struct tx3904sio);
    247   set_hw_data (me, controller);
    248   set_hw_io_read_buffer (me, tx3904sio_io_read_buffer);
    249   set_hw_io_write_buffer (me, tx3904sio_io_write_buffer);
    250   set_hw_ports (me, tx3904sio_ports);
    251   set_hw_port_event (me, tx3904sio_port_event);
    252 
    253   /* Preset defaults */
    254   controller->backend = sio_stdio;
    255 
    256   /* Attach ourself to our parent bus */
    257   attach_tx3904sio_regs (me, controller);
    258 
    259   /* Initialize to reset state */
    260   tx3904sio_fifo_reset(me, & controller->rx_fifo);
    261   tx3904sio_fifo_reset(me, & controller->tx_fifo);
    262   controller->slsr = controller->sdicr
    263     = controller->sdisr = controller->sfcr
    264     = controller->sbgr = 0;
    265   controller->slcr = 0x40000000; /* set TWUB */
    266   controller->sbgr = 0x03ff0000; /* set BCLK=3, BRD=FF */
    267   controller->poll_event = NULL;
    268 }
    269 
    270 
    271 
    272 /* An event arrives on an interrupt port */
    273 
    274 static void
    275 tx3904sio_port_event (struct hw *me,
    276 		     int my_port,
    277 		     struct hw *source,
    278 		     int source_port,
    279 		     int level)
    280 {
    281   struct tx3904sio *controller = hw_data (me);
    282 
    283   switch (my_port)
    284     {
    285     case RESET_PORT:
    286       {
    287 	HW_TRACE ((me, "reset"));
    288 
    289 	tx3904sio_fifo_reset(me, & controller->rx_fifo);
    290 	tx3904sio_fifo_reset(me, & controller->tx_fifo);
    291 	controller->slsr = controller->sdicr
    292 	  = controller->sdisr = controller->sfcr
    293 	  = controller->sbgr = 0;
    294 	controller->slcr = 0x40000000; /* set TWUB */
    295 	controller->sbgr = 0x03ff0000; /* set BCLK=3, BRD=FF */
    296 	/* Don't interfere with I/O poller. */
    297 	break;
    298       }
    299 
    300     default:
    301       hw_abort (me, "Event on unknown port %d", my_port);
    302       break;
    303     }
    304 }
    305 
    306 
    307 /* generic read/write */
    308 
    309 static unsigned
    310 tx3904sio_io_read_buffer (struct hw *me,
    311 			 void *dest,
    312 			 int space,
    313 			 unsigned_word base,
    314 			 unsigned nr_bytes)
    315 {
    316   struct tx3904sio *controller = hw_data (me);
    317   unsigned byte;
    318 
    319   HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
    320 
    321   /* tickle fifos */
    322   tx3904sio_tickle(me);
    323 
    324   for (byte = 0; byte < nr_bytes; byte++)
    325     {
    326       address_word address = base + byte;
    327       int reg_number = (address - controller->base_address) / 4;
    328       int reg_offset = (address - controller->base_address) % 4;
    329       unsigned_4 register_value; /* in target byte order */
    330 
    331       /* fill in entire register_value word */
    332       switch (reg_number)
    333 	{
    334 	case SLCR_REG: register_value = controller->slcr; break;
    335 	case SLSR_REG: register_value = controller->slsr; break;
    336 	case SDICR_REG: register_value = controller->sdicr; break;
    337 	case SDISR_REG: register_value = controller->sdisr; break;
    338 	case SFCR_REG: register_value = controller->sfcr; break;
    339 	case SBGR_REG: register_value = controller->sbgr; break;
    340 	case TFIFO_REG: register_value = 0; break;
    341 	case SFIFO_REG:
    342 	  /* consume rx fifo for MS byte */
    343 	  if(reg_offset == 0 && tx3904sio_fifo_nonempty(me, & controller->rx_fifo))
    344 	    register_value = (tx3904sio_fifo_pop(me, & controller->rx_fifo) << 24);
    345 	  else
    346 	    register_value = 0;
    347 	  break;
    348 	default: register_value = 0;
    349 	}
    350 
    351       /* write requested byte out */
    352       register_value = H2T_4(register_value);
    353       /* HW_TRACE ((me, "byte %d %02x", reg_offset, ((char*)& register_value)[reg_offset])); */
    354       memcpy ((char*) dest + byte, ((char*)& register_value)+reg_offset, 1);
    355     }
    356 
    357   return nr_bytes;
    358 }
    359 
    360 
    361 
    362 static unsigned
    363 tx3904sio_io_write_buffer (struct hw *me,
    364 			  const void *source,
    365 			  int space,
    366 			  unsigned_word base,
    367 			  unsigned nr_bytes)
    368 {
    369   struct tx3904sio *controller = hw_data (me);
    370   unsigned byte;
    371 
    372   HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
    373   for (byte = 0; byte < nr_bytes; byte++)
    374     {
    375       address_word address = base + byte;
    376       unsigned_1 write_byte = ((const unsigned char*) source)[byte];
    377       int reg_number = (address - controller->base_address) / 4;
    378       int reg_offset = 3 - (address - controller->base_address) % 4;
    379 
    380       /* HW_TRACE ((me, "byte %d %02x", reg_offset, write_byte)); */
    381 
    382       /* fill in entire register_value word */
    383       switch (reg_number)
    384 	{
    385 	case SLCR_REG:
    386 	  SLCR_SET_BYTE(controller, reg_offset, write_byte);
    387 	  break;
    388 
    389 	case SLSR_REG: /* unwriteable */ break;
    390 
    391 	case SDICR_REG:
    392 	  {
    393 	    unsigned_4 last_int, next_int;
    394 
    395 	    /* deassert interrupt upon clear */
    396 	    last_int = controller->sdisr & controller->sdicr;
    397 	    /* HW_TRACE ((me, "sdicr - sdisr %08x sdicr %08x",
    398 	       controller->sdisr, controller->sdicr)); */
    399 	    SDICR_SET_BYTE(controller, reg_offset, write_byte);
    400 	    /* HW_TRACE ((me, "sdicr + sdisr %08x sdicr %08x",
    401 	       controller->sdisr, controller->sdicr)); */
    402 	    next_int = controller->sdisr & controller->sdicr;
    403 
    404 	    if(SDICR_GET_SDMAE(controller))
    405 	      hw_abort(me, "Cannot support DMA-driven sio.");
    406 
    407 	    if(~last_int & next_int) /* any bits set? */
    408 	      hw_port_event(me, INT_PORT, 1);
    409 	    if(last_int & ~next_int) /* any bits cleared? */
    410 	      hw_port_event(me, INT_PORT, 0);
    411 	  }
    412 	break;
    413 
    414 	case SDISR_REG:
    415 	  {
    416 	    unsigned_4 last_int, next_int;
    417 
    418 	    /* deassert interrupt upon clear */
    419 	    last_int = controller->sdisr & controller->sdicr;
    420 	    /* HW_TRACE ((me, "sdisr - sdisr %08x sdicr %08x",
    421 	       controller->sdisr, controller->sdicr)); */
    422 	    SDISR_CLEAR_FLAG_BYTE(controller, reg_offset, write_byte);
    423 	    /* HW_TRACE ((me, "sdisr + sdisr %08x sdicr %08x",
    424 	       controller->sdisr, controller->sdicr)); */
    425 	    next_int = controller->sdisr & controller->sdicr;
    426 
    427 	    if(~last_int & next_int) /* any bits set? */
    428 	      hw_port_event(me, INT_PORT, 1);
    429 	    if(last_int & ~next_int) /* any bits cleared? */
    430 	      hw_port_event(me, INT_PORT, 0);
    431 	  }
    432 	break;
    433 
    434 	case SFCR_REG:
    435 	  SFCR_SET_BYTE(controller, reg_offset, write_byte);
    436 	  if(SFCR_GET_FRSTE(controller))
    437 	    {
    438 	      if(SFCR_GET_TFRST(controller)) tx3904sio_fifo_reset(me, & controller->tx_fifo);
    439 	      if(SFCR_GET_RFRST(controller)) tx3904sio_fifo_reset(me, & controller->rx_fifo);
    440 	    }
    441 	  break;
    442 
    443 	case SBGR_REG:
    444 	  SBGR_SET_BYTE(controller, reg_offset, write_byte);
    445 	  break;
    446 
    447 	case SFIFO_REG: /* unwriteable */ break;
    448 
    449 	case TFIFO_REG:
    450 	  if(reg_offset == 3) /* first byte */
    451 	    tx3904sio_fifo_push(me, & controller->tx_fifo, write_byte);
    452 	  break;
    453 
    454 	default:
    455 	  HW_TRACE ((me, "write to illegal register %d", reg_number));
    456 	}
    457     } /* loop over bytes */
    458 
    459   /* tickle fifos */
    460   tx3904sio_tickle(me);
    461 
    462   return nr_bytes;
    463 }
    464 
    465 
    466 
    467 
    468 
    469 
    470 /* Send enqueued characters from tx_fifo and trigger TX interrupt.
    471 Receive characters into rx_fifo and trigger RX interrupt. */
    472 void
    473 tx3904sio_tickle(struct hw *me)
    474 {
    475   struct tx3904sio* controller = hw_data(me);
    476   int c;
    477   char cc;
    478   unsigned_4 last_int, next_int;
    479 
    480   /* HW_TRACE ((me, "tickle backend: %02x", controller->backend)); */
    481   switch(controller->backend)
    482     {
    483     case sio_tcp:
    484 
    485       while(tx3904sio_fifo_nonempty(me, & controller->tx_fifo))
    486 	{
    487 	  cc = tx3904sio_fifo_pop(me, & controller->tx_fifo);
    488 	  dv_sockser_write(hw_system(me), cc);
    489 	  HW_TRACE ((me, "tcp output: %02x", cc));
    490 	}
    491 
    492       c = dv_sockser_read(hw_system(me));
    493       while(c != -1)
    494 	{
    495 	  cc = (char) c;
    496 	  HW_TRACE ((me, "tcp input: %02x", cc));
    497 	  tx3904sio_fifo_push(me, & controller->rx_fifo, cc);
    498 	  c = dv_sockser_read(hw_system(me));
    499 	}
    500       break;
    501 
    502     case sio_stdio:
    503 
    504       while(tx3904sio_fifo_nonempty(me, & controller->tx_fifo))
    505 	{
    506 	  cc = tx3904sio_fifo_pop(me, & controller->tx_fifo);
    507 	  sim_io_write_stdout(hw_system(me), & cc, 1);
    508 	  sim_io_flush_stdout(hw_system(me));
    509 	  HW_TRACE ((me, "stdio output: %02x", cc));
    510 	}
    511 
    512       c = sim_io_poll_read(hw_system(me), 0 /* stdin */, & cc, 1);
    513       while(c == 1)
    514 	{
    515 	  HW_TRACE ((me, "stdio input: %02x", cc));
    516 	  tx3904sio_fifo_push(me, & controller->rx_fifo, cc);
    517 	  c = sim_io_poll_read(hw_system(me), 0 /* stdin */, & cc, 1);
    518 	}
    519 
    520       break;
    521 
    522     default:
    523       hw_abort(me, "Illegal backend mode: %d", controller->backend);
    524     }
    525 
    526   /* Update RDIS / TDIS flags */
    527   last_int = controller->sdisr & controller->sdicr;
    528   /* HW_TRACE ((me, "tickle - sdisr %08x sdicr %08x", controller->sdisr, controller->sdicr)); */
    529   if(tx3904sio_fifo_nonempty(me, & controller->rx_fifo))
    530     SDISR_SET_RDIS(controller);
    531   if(! tx3904sio_fifo_nonempty(me, & controller->tx_fifo))
    532     SDISR_SET_TDIS(controller);
    533   next_int = controller->sdisr & controller->sdicr;
    534   /* HW_TRACE ((me, "tickle + sdisr %08x sdicr %08x", controller->sdisr, controller->sdicr)); */
    535 
    536   if(~last_int & next_int) /* any bits set? */
    537     hw_port_event(me, INT_PORT, 1);
    538   if(last_int & ~next_int) /* any bits cleared? */
    539     hw_port_event(me, INT_PORT, 0);
    540 
    541   /* Add periodic polling for this port, if it's not already going. */
    542   if(controller->poll_event == NULL)
    543     {
    544       controller->poll_event = hw_event_queue_schedule (me, 1000,
    545 							tx3904sio_poll, NULL);
    546 
    547     }
    548 }
    549 
    550 
    551 
    552 
    553 int
    554 tx3904sio_fifo_nonempty(struct hw* me, struct tx3904sio_fifo* fifo)
    555 {
    556   /* HW_TRACE ((me, "fifo used: %d", fifo->used)); */
    557   return(fifo->used > 0);
    558 }
    559 
    560 
    561 char
    562 tx3904sio_fifo_pop(struct hw* me, struct tx3904sio_fifo* fifo)
    563 {
    564   char it;
    565   ASSERT(fifo->used > 0);
    566   ASSERT(fifo->buffer != NULL);
    567   it = fifo->buffer[0];
    568   memcpy(& fifo->buffer[0], & fifo->buffer[1], fifo->used - 1);
    569   fifo->used --;
    570   /* HW_TRACE ((me, "pop fifo -> %02x", it)); */
    571   return it;
    572 }
    573 
    574 
    575 void
    576 tx3904sio_fifo_push(struct hw* me, struct tx3904sio_fifo* fifo, char it)
    577 {
    578   /* HW_TRACE ((me, "push %02x -> fifo", it)); */
    579   if(fifo->size == fifo->used) /* full */
    580     {
    581       int next_size = fifo->size * 2 + 16;
    582       char* next_buf = zalloc(next_size);
    583       memcpy(next_buf, fifo->buffer, fifo->used);
    584 
    585       if(fifo->buffer != NULL) free(fifo->buffer);
    586       fifo->buffer = next_buf;
    587       fifo->size = next_size;
    588     }
    589 
    590   fifo->buffer[fifo->used] = it;
    591   fifo->used ++;
    592 }
    593 
    594 
    595 void
    596 tx3904sio_fifo_reset(struct hw* me, struct tx3904sio_fifo* fifo)
    597 {
    598   /* HW_TRACE ((me, "reset fifo")); */
    599   fifo->used = 0;
    600   fifo->size = 0;
    601   free(fifo->buffer);
    602   fifo->buffer = 0;
    603 }
    604 
    605 
    606 void
    607 tx3904sio_poll(struct hw* me, void* ignored)
    608 {
    609   struct tx3904sio* controller = hw_data (me);
    610   tx3904sio_tickle (me);
    611   hw_event_queue_deschedule (me, controller->poll_event);
    612   controller->poll_event = hw_event_queue_schedule (me, 1000,
    613 						    tx3904sio_poll, NULL);
    614 }
    615 
    616 
    617 
    618 const struct hw_descriptor dv_tx3904sio_descriptor[] = {
    619   { "tx3904sio", tx3904sio_finish, },
    620   { NULL },
    621 };
    622