Changeset 70a9451 in rtems


Ignore:
Timestamp:
Jul 25, 1998, 4:53:21 PM (21 years ago)
Author:
Joel Sherrill <joel.sherrill@…>
Branches:
4.10, 4.11, 4.8, 4.9, master
Children:
ccfa60b
Parents:
adedb057
Message:

Rewrote NS16550 TX interrupt processing to use termios for the buffer
and manage the interrupt sources like the other drivers. This
let use remove the ns16550_flush() routine.

Location:
c/src
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • c/src/lib/libchip/serial/STATUS

    radedb057 r70a9451  
    3636  well.
    3737
    38 + Interrupt code needs to be reworked to eliminate Ring_buffer usage.  This
    39   will probably require managing the interrupt mask register as is
    40   done in the mc68681 and z85c30 drivers.
     38+ Interrupt code has been reworked to not use ring buffer and may be broken
     39  as it has not been tested since this was done.
    4140
    4241+ Missing set attributes function.
  • c/src/lib/libchip/serial/ns16550.c

    radedb057 r70a9451  
    4949  libchip_serial_default_probe,   /* deviceProbe */
    5050  ns16550_open,                   /* deviceFirstOpen */
    51   ns16550_flush,                  /* deviceLastClose */
     51  NULL,                           /* deviceLastClose */
    5252  NULL,                           /* deviceRead */
    5353  ns16550_write_support_int,      /* deviceWrite */
     
    9898
    9999  (*setReg)(pNS16550, NS16550_LINE_CONTROL, 0x0);
    100   (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, 0x0);
     100  ns16550_enable_interrupts(minor, NS16550_DISABLE_ALL_INTR);
    101101
    102102  /* Set the divisor latch and set the baud rate. */
     
    105105  ucDataByte = SP_LINE_DLAB;
    106106  (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
     107
     108  /* XXX */
    107109  (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, ulBaudDivisor&0xff);
    108110  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (ulBaudDivisor>>8)&0xff);
     
    120122  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
    121123
    122   /*
    123    * Disable interrupts
    124    */
    125   ucDataByte = 0;
    126   (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, ucDataByte);
     124  ns16550_enable_interrupts(minor, NS16550_DISABLE_ALL_INTR);
    127125
    128126  /* Set data terminal ready. */
     
    170168 *  ns16550_write_polled
    171169 */
     170
    172171NS16550_STATIC void ns16550_write_polled(
    173172  int   minor,
     
    336335  volatile unsigned8      ucLineStatus;
    337336  volatile unsigned8      ucInterruptId;
    338   char                    cChar;
     337  unsigned char           cChar;
    339338  getRegister_f           getReg;
    340339  setRegister_f           setReg;
     
    361360    }
    362361
     362    /*
     363     *  TX all the characters we can
     364     */
     365
    363366    while(TRUE) {
    364       if(Ring_buffer_Is_empty(&Console_Port_Data[minor].TxBuffer)) {
    365         Console_Port_Data[minor].bActive=FALSE;
    366         if(Console_Port_Tbl[minor].pDeviceFlow !=&ns16550_flow_RTSCTS) {
     367        ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
     368        if(~ucLineStatus & SP_LSR_THOLD) {
     369          /*
     370           * We'll get another interrupt when
     371           * the transmitter holding reg. becomes
     372           * free again
     373           */
     374          break;
     375        }
     376
     377#if 0
     378        /* XXX flow control not completely supported in libchip */
     379
     380        if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
    367381          ns16550_negate_RTS(minor);
    368382        }
    369 
    370         /*
    371          * There is no data to transmit
    372          */
     383#endif
     384
     385      if (!rtems_termios_dequeue_characters(
     386                  Console_Port_Data[minor].termios_data, 1)) {
     387        if (Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
     388          ns16550_negate_RTS(minor);
     389        }
     390        Console_Port_Data[minor].bActive = FALSE;
     391        ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
    373392        break;
    374393      }
    375394
    376       ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
    377       if(~ucLineStatus & SP_LSR_THOLD) {
    378         /*
    379          * We'll get another interrupt when
    380          * the transmitter holding reg. becomes
    381          * free again
    382          */
    383         break;
    384       }
    385 
    386       Ring_buffer_Remove_character( &Console_Port_Data[minor].TxBuffer, cChar);
    387       /*
    388        * transmit character
    389        */
    390       (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, cChar);
     395      ucInterruptId = (*getReg)(pNS16550, NS16550_INTERRUPT_ID);
    391396    }
    392 
    393     ucInterruptId = (*getReg)(pNS16550, NS16550_INTERRUPT_ID);
    394   }
    395   while((ucInterruptId&0xf)!=0x1);
     397  } while((ucInterruptId&0xf)!=0x1);
    396398}
    397399
     
    411413
    412414/*
    413  *  ns16550_flush
    414  */
    415 NS16550_STATIC int ns16550_flush(int major, int minor, void *arg)
    416 {
    417   while(!Ring_buffer_Is_empty(&Console_Port_Data[minor].TxBuffer)) {
    418     /*
    419      * Yield while we wait
    420      */
    421     if(_System_state_Is_up(_System_state_Get())) {
    422       rtems_task_wake_after(RTEMS_YIELD_PROCESSOR);
    423     }
    424   }
    425 
    426   ns16550_close(major, minor, arg);
    427 
    428   return(RTEMS_SUCCESSFUL);
     415 *  ns16550_enable_interrupts
     416 *
     417 *  This routine initializes the port to have the specified interrupts masked.
     418 */
     419
     420NS16550_STATIC void ns16550_enable_interrupts(
     421  int minor,
     422  int mask
     423)
     424{
     425  unsigned32     pNS16550;
     426  setRegister_f  setReg;
     427
     428  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
     429  setReg   = Console_Port_Tbl[minor].setRegister;
     430
     431  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, mask);
    429432}
    430433
     
    432435 *  ns16550_initialize_interrupts
    433436 *
    434  *  This routine initializes the console's receive and transmit
    435  *  ring buffers and loads the appropriate vectors to handle the interrupts.
    436  *
    437  *  Input parameters:  NONE
    438  *
    439  *  Output parameters: NONE
    440  *
    441  *  Return values:     NONE
    442  */
    443 
    444 NS16550_STATIC void ns16550_enable_interrupts(
    445   int minor
    446 )
    447 {
    448   unsigned32            pNS16550;
    449   unsigned8             ucDataByte;
    450   setRegister_f           setReg;
    451 
    452   pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
    453   setReg   = Console_Port_Tbl[minor].setRegister;
    454 
    455   /*
    456    * Enable interrupts
    457    */
    458   ucDataByte = SP_INT_RX_ENABLE | SP_INT_TX_ENABLE;
    459   (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, ucDataByte);
    460 
    461 }
     437 *  This routine initializes the port to operate in interrupt driver mode.
     438 */
    462439
    463440NS16550_STATIC void ns16550_initialize_interrupts(int minor)
     
    465442  ns16550_init(minor);
    466443
    467   Ring_buffer_Initialize(&Console_Port_Data[minor].TxBuffer);
    468 
    469444  Console_Port_Data[minor].bActive = FALSE;
    470445
    471446  set_vector(ns16550_isr, Console_Port_Tbl[minor].ulIntVector, 1);
    472447
    473   ns16550_enable_interrupts(minor);
     448  ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR);
    474449}
    475450
     
    478453 *
    479454 *  Console Termios output entry point.
    480  *
    481  */
     455 */
     456
    482457NS16550_STATIC int ns16550_write_support_int(
    483458  int   minor,
     
    486461)
    487462{
    488   int i;
    489   unsigned32 Irql;
    490 
    491   for(i=0; i<len;) {
    492     if(Ring_buffer_Is_full(&Console_Port_Data[minor].TxBuffer)) {
    493       if(!Console_Port_Data[minor].bActive) {
    494         /*
    495          * Wake up the device
    496          */
    497         rtems_interrupt_disable(Irql);
    498         Console_Port_Data[minor].bActive = TRUE;
    499         if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
    500           ns16550_assert_RTS(minor);
    501         }
    502         ns16550_process(minor);
    503         rtems_interrupt_enable(Irql);
    504       } else {
    505         /*
    506          * Yield
    507          */
    508         rtems_task_wake_after(RTEMS_YIELD_PROCESSOR);
    509       }
    510 
    511       /*
    512        * Wait for ring buffer to empty
    513        */
    514       continue;
     463  unsigned32     Irql;
     464  unsigned32     pNS16550;
     465  setRegister_f  setReg;
     466
     467  setReg   = Console_Port_Tbl[minor].setRegister;
     468  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
     469
     470  /*
     471   *  We are using interrupt driven output and termios only sends us
     472   *  one character at a time.
     473   */
     474
     475  if ( !len )
     476    return 0;
     477
     478  if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
     479    ns16550_assert_RTS(minor);
     480  }
     481
     482  rtems_interrupt_disable(Irql);
     483    if ( Console_Port_Data[minor].bActive == FALSE) {
     484      Console_Port_Data[minor].bActive = TRUE;
     485      ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR);
    515486    }
    516     else {
    517       Ring_buffer_Add_character( &Console_Port_Data[minor].TxBuffer, buf[i]);
    518       i++;
    519     }
    520   }
    521 
    522   /*
    523    * Ensure that characters are on the way
    524    */
    525   if(!Console_Port_Data[minor].bActive) {
    526     /*
    527      * Wake up the device
    528      */
    529     rtems_interrupt_disable(Irql);
    530     Console_Port_Data[minor].bActive = TRUE;
    531     if(Console_Port_Tbl[minor].pDeviceFlow !=&ns16550_flow_RTSCTS) {
    532       ns16550_assert_RTS(minor);
    533     }
    534     ns16550_process(minor);
    535     rtems_interrupt_enable(Irql);
    536   }
    537 
    538   return (len);
     487    (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, *buf);
     488  rtems_interrupt_enable(Irql);
     489
     490  return 1;
    539491}
    540492
     
    545497 *
    546498 */
     499
    547500NS16550_STATIC int ns16550_write_support_polled(
    548501  int         minor,
  • c/src/lib/libchip/serial/ns16550_p.h

    radedb057 r70a9451  
    8383#define SP_INT_MS_ENABLE  0x08
    8484
     85#define NS16550_ENABLE_ALL_INTR           (SP_INT_RX_ENABLE | SP_INT_TX_ENABLE)
     86#define NS16550_DISABLE_ALL_INTR          0x00
     87#define NS16550_ENABLE_ALL_INTR_EXCEPT_TX (SP_INT_RX_ENABLE)
     88
    8589/*
    8690 * Define serial port interrupt id register structure.
     
    202206NS16550_STATIC void ns16550_initialize_interrupts(int minor);
    203207
    204 NS16550_STATIC int ns16550_flush(int major, int minor, void *arg);
    205 
    206208NS16550_STATIC int ns16550_write_support_int(
    207209  int   minor,
     
    220222);
    221223
     224NS16550_STATIC void ns16550_enable_interrupts(
     225  int minor,
     226  int mask
     227);
     228
    222229#ifdef __cplusplus
    223230}
  • c/src/libchip/serial/STATUS

    radedb057 r70a9451  
    3636  well.
    3737
    38 + Interrupt code needs to be reworked to eliminate Ring_buffer usage.  This
    39   will probably require managing the interrupt mask register as is
    40   done in the mc68681 and z85c30 drivers.
     38+ Interrupt code has been reworked to not use ring buffer and may be broken
     39  as it has not been tested since this was done.
    4140
    4241+ Missing set attributes function.
  • c/src/libchip/serial/ns16550.c

    radedb057 r70a9451  
    4949  libchip_serial_default_probe,   /* deviceProbe */
    5050  ns16550_open,                   /* deviceFirstOpen */
    51   ns16550_flush,                  /* deviceLastClose */
     51  NULL,                           /* deviceLastClose */
    5252  NULL,                           /* deviceRead */
    5353  ns16550_write_support_int,      /* deviceWrite */
     
    9898
    9999  (*setReg)(pNS16550, NS16550_LINE_CONTROL, 0x0);
    100   (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, 0x0);
     100  ns16550_enable_interrupts(minor, NS16550_DISABLE_ALL_INTR);
    101101
    102102  /* Set the divisor latch and set the baud rate. */
     
    105105  ucDataByte = SP_LINE_DLAB;
    106106  (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
     107
     108  /* XXX */
    107109  (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, ulBaudDivisor&0xff);
    108110  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (ulBaudDivisor>>8)&0xff);
     
    120122  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
    121123
    122   /*
    123    * Disable interrupts
    124    */
    125   ucDataByte = 0;
    126   (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, ucDataByte);
     124  ns16550_enable_interrupts(minor, NS16550_DISABLE_ALL_INTR);
    127125
    128126  /* Set data terminal ready. */
     
    170168 *  ns16550_write_polled
    171169 */
     170
    172171NS16550_STATIC void ns16550_write_polled(
    173172  int   minor,
     
    336335  volatile unsigned8      ucLineStatus;
    337336  volatile unsigned8      ucInterruptId;
    338   char                    cChar;
     337  unsigned char           cChar;
    339338  getRegister_f           getReg;
    340339  setRegister_f           setReg;
     
    361360    }
    362361
     362    /*
     363     *  TX all the characters we can
     364     */
     365
    363366    while(TRUE) {
    364       if(Ring_buffer_Is_empty(&Console_Port_Data[minor].TxBuffer)) {
    365         Console_Port_Data[minor].bActive=FALSE;
    366         if(Console_Port_Tbl[minor].pDeviceFlow !=&ns16550_flow_RTSCTS) {
     367        ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
     368        if(~ucLineStatus & SP_LSR_THOLD) {
     369          /*
     370           * We'll get another interrupt when
     371           * the transmitter holding reg. becomes
     372           * free again
     373           */
     374          break;
     375        }
     376
     377#if 0
     378        /* XXX flow control not completely supported in libchip */
     379
     380        if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
    367381          ns16550_negate_RTS(minor);
    368382        }
    369 
    370         /*
    371          * There is no data to transmit
    372          */
     383#endif
     384
     385      if (!rtems_termios_dequeue_characters(
     386                  Console_Port_Data[minor].termios_data, 1)) {
     387        if (Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
     388          ns16550_negate_RTS(minor);
     389        }
     390        Console_Port_Data[minor].bActive = FALSE;
     391        ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
    373392        break;
    374393      }
    375394
    376       ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
    377       if(~ucLineStatus & SP_LSR_THOLD) {
    378         /*
    379          * We'll get another interrupt when
    380          * the transmitter holding reg. becomes
    381          * free again
    382          */
    383         break;
    384       }
    385 
    386       Ring_buffer_Remove_character( &Console_Port_Data[minor].TxBuffer, cChar);
    387       /*
    388        * transmit character
    389        */
    390       (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, cChar);
     395      ucInterruptId = (*getReg)(pNS16550, NS16550_INTERRUPT_ID);
    391396    }
    392 
    393     ucInterruptId = (*getReg)(pNS16550, NS16550_INTERRUPT_ID);
    394   }
    395   while((ucInterruptId&0xf)!=0x1);
     397  } while((ucInterruptId&0xf)!=0x1);
    396398}
    397399
     
    411413
    412414/*
    413  *  ns16550_flush
    414  */
    415 NS16550_STATIC int ns16550_flush(int major, int minor, void *arg)
    416 {
    417   while(!Ring_buffer_Is_empty(&Console_Port_Data[minor].TxBuffer)) {
    418     /*
    419      * Yield while we wait
    420      */
    421     if(_System_state_Is_up(_System_state_Get())) {
    422       rtems_task_wake_after(RTEMS_YIELD_PROCESSOR);
    423     }
    424   }
    425 
    426   ns16550_close(major, minor, arg);
    427 
    428   return(RTEMS_SUCCESSFUL);
     415 *  ns16550_enable_interrupts
     416 *
     417 *  This routine initializes the port to have the specified interrupts masked.
     418 */
     419
     420NS16550_STATIC void ns16550_enable_interrupts(
     421  int minor,
     422  int mask
     423)
     424{
     425  unsigned32     pNS16550;
     426  setRegister_f  setReg;
     427
     428  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
     429  setReg   = Console_Port_Tbl[minor].setRegister;
     430
     431  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, mask);
    429432}
    430433
     
    432435 *  ns16550_initialize_interrupts
    433436 *
    434  *  This routine initializes the console's receive and transmit
    435  *  ring buffers and loads the appropriate vectors to handle the interrupts.
    436  *
    437  *  Input parameters:  NONE
    438  *
    439  *  Output parameters: NONE
    440  *
    441  *  Return values:     NONE
    442  */
    443 
    444 NS16550_STATIC void ns16550_enable_interrupts(
    445   int minor
    446 )
    447 {
    448   unsigned32            pNS16550;
    449   unsigned8             ucDataByte;
    450   setRegister_f           setReg;
    451 
    452   pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
    453   setReg   = Console_Port_Tbl[minor].setRegister;
    454 
    455   /*
    456    * Enable interrupts
    457    */
    458   ucDataByte = SP_INT_RX_ENABLE | SP_INT_TX_ENABLE;
    459   (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, ucDataByte);
    460 
    461 }
     437 *  This routine initializes the port to operate in interrupt driver mode.
     438 */
    462439
    463440NS16550_STATIC void ns16550_initialize_interrupts(int minor)
     
    465442  ns16550_init(minor);
    466443
    467   Ring_buffer_Initialize(&Console_Port_Data[minor].TxBuffer);
    468 
    469444  Console_Port_Data[minor].bActive = FALSE;
    470445
    471446  set_vector(ns16550_isr, Console_Port_Tbl[minor].ulIntVector, 1);
    472447
    473   ns16550_enable_interrupts(minor);
     448  ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR);
    474449}
    475450
     
    478453 *
    479454 *  Console Termios output entry point.
    480  *
    481  */
     455 */
     456
    482457NS16550_STATIC int ns16550_write_support_int(
    483458  int   minor,
     
    486461)
    487462{
    488   int i;
    489   unsigned32 Irql;
    490 
    491   for(i=0; i<len;) {
    492     if(Ring_buffer_Is_full(&Console_Port_Data[minor].TxBuffer)) {
    493       if(!Console_Port_Data[minor].bActive) {
    494         /*
    495          * Wake up the device
    496          */
    497         rtems_interrupt_disable(Irql);
    498         Console_Port_Data[minor].bActive = TRUE;
    499         if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
    500           ns16550_assert_RTS(minor);
    501         }
    502         ns16550_process(minor);
    503         rtems_interrupt_enable(Irql);
    504       } else {
    505         /*
    506          * Yield
    507          */
    508         rtems_task_wake_after(RTEMS_YIELD_PROCESSOR);
    509       }
    510 
    511       /*
    512        * Wait for ring buffer to empty
    513        */
    514       continue;
     463  unsigned32     Irql;
     464  unsigned32     pNS16550;
     465  setRegister_f  setReg;
     466
     467  setReg   = Console_Port_Tbl[minor].setRegister;
     468  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
     469
     470  /*
     471   *  We are using interrupt driven output and termios only sends us
     472   *  one character at a time.
     473   */
     474
     475  if ( !len )
     476    return 0;
     477
     478  if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
     479    ns16550_assert_RTS(minor);
     480  }
     481
     482  rtems_interrupt_disable(Irql);
     483    if ( Console_Port_Data[minor].bActive == FALSE) {
     484      Console_Port_Data[minor].bActive = TRUE;
     485      ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR);
    515486    }
    516     else {
    517       Ring_buffer_Add_character( &Console_Port_Data[minor].TxBuffer, buf[i]);
    518       i++;
    519     }
    520   }
    521 
    522   /*
    523    * Ensure that characters are on the way
    524    */
    525   if(!Console_Port_Data[minor].bActive) {
    526     /*
    527      * Wake up the device
    528      */
    529     rtems_interrupt_disable(Irql);
    530     Console_Port_Data[minor].bActive = TRUE;
    531     if(Console_Port_Tbl[minor].pDeviceFlow !=&ns16550_flow_RTSCTS) {
    532       ns16550_assert_RTS(minor);
    533     }
    534     ns16550_process(minor);
    535     rtems_interrupt_enable(Irql);
    536   }
    537 
    538   return (len);
     487    (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, *buf);
     488  rtems_interrupt_enable(Irql);
     489
     490  return 1;
    539491}
    540492
     
    545497 *
    546498 */
     499
    547500NS16550_STATIC int ns16550_write_support_polled(
    548501  int         minor,
  • c/src/libchip/serial/ns16550_p.h

    radedb057 r70a9451  
    8383#define SP_INT_MS_ENABLE  0x08
    8484
     85#define NS16550_ENABLE_ALL_INTR           (SP_INT_RX_ENABLE | SP_INT_TX_ENABLE)
     86#define NS16550_DISABLE_ALL_INTR          0x00
     87#define NS16550_ENABLE_ALL_INTR_EXCEPT_TX (SP_INT_RX_ENABLE)
     88
    8589/*
    8690 * Define serial port interrupt id register structure.
     
    202206NS16550_STATIC void ns16550_initialize_interrupts(int minor);
    203207
    204 NS16550_STATIC int ns16550_flush(int major, int minor, void *arg);
    205 
    206208NS16550_STATIC int ns16550_write_support_int(
    207209  int   minor,
     
    220222);
    221223
     224NS16550_STATIC void ns16550_enable_interrupts(
     225  int minor,
     226  int mask
     227);
     228
    222229#ifdef __cplusplus
    223230}
Note: See TracChangeset for help on using the changeset viewer.