source: rtems/c/src/libchip/serial/ns16550.c @ dbfafb8f

4.104.114.84.95
Last change on this file since dbfafb8f was cebad96, checked in by Jennifer Averett <Jennifer.Averett@…>, on 04/25/05 at 14:22:55

2005-04-25 Jennifer Averett <jennifer.averett@…>

  • libchip/serial/ns16550.c: Fixed warnings generated in various powerpc configurations
  • Property mode set to 100644
File size: 17.9 KB
Line 
1/*
2 *  This file contains the TTY driver for the National Semiconductor NS16550.
3 *
4 *  This part is widely cloned and second sourced.  It is found in a number
5 *  of "Super IO" controllers.
6 *
7 *  COPYRIGHT (c) 1998 by Radstone Technology
8 *
9 *
10 * THIS FILE IS PROVIDED TO YOU, THE USER, "AS IS", WITHOUT WARRANTY OF ANY
11 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
12 * IMPLIED WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK
13 * AS TO THE QUALITY AND PERFORMANCE OF ALL CODE IN THIS FILE IS WITH YOU.
14 *
15 * You are hereby granted permission to use, copy, modify, and distribute
16 * this file, provided that this notice, plus the above copyright notice
17 * and disclaimer, appears in all copies. Radstone Technology will provide
18 * no support for this code.
19 *
20 *  This driver uses the termios pseudo driver.
21 */
22
23#include <rtems.h>
24#include <rtems/libio.h>
25#include <stdlib.h>
26#include <rtems/ringbuf.h>
27
28#include <libchip/serial.h>
29#include <libchip/sersupp.h>
30#include <rtems/bspIo.h>
31#include "ns16550_p.h"
32
33/*
34 * Flow control is only supported when using interrupts
35 */
36
37console_flow ns16550_flow_RTSCTS = {
38  ns16550_negate_RTS,             /* deviceStopRemoteTx */
39  ns16550_assert_RTS              /* deviceStartRemoteTx */
40};
41
42console_flow ns16550_flow_DTRCTS = {
43  ns16550_negate_DTR,             /* deviceStopRemoteTx */
44  ns16550_assert_DTR              /* deviceStartRemoteTx */
45};
46
47console_fns ns16550_fns = {
48  libchip_serial_default_probe,   /* deviceProbe */
49  ns16550_open,                   /* deviceFirstOpen */
50  NULL,                           /* deviceLastClose */
51  NULL,                           /* deviceRead */
52  ns16550_write_support_int,      /* deviceWrite */
53  ns16550_initialize_interrupts,  /* deviceInitialize */
54  ns16550_write_polled,           /* deviceWritePolled */
55  ns16550_set_attributes,         /* deviceSetAttributes */
56  TRUE                            /* deviceOutputUsesInterrupts */
57};
58
59console_fns ns16550_fns_polled = {
60  libchip_serial_default_probe,        /* deviceProbe */
61  ns16550_open,                        /* deviceFirstOpen */
62  ns16550_close,                       /* deviceLastClose */
63  ns16550_inbyte_nonblocking_polled,   /* deviceRead */
64  ns16550_write_support_polled,        /* deviceWrite */
65  ns16550_init,                        /* deviceInitialize */
66  ns16550_write_polled,                /* deviceWritePolled */
67  ns16550_set_attributes,              /* deviceSetAttributes */
68  FALSE                                /* deviceOutputUsesInterrupts */
69};
70
71#if defined(__PPC__)
72#ifdef _OLD_EXCEPTIONS
73extern void set_vector( rtems_isr_entry, rtems_vector_number, int );
74#else
75#include <bsp/irq.h>
76#endif
77#endif
78
79/*
80 *  ns16550_init
81 */
82
83NS16550_STATIC void ns16550_init(int minor)
84{
85  uint32_t                pNS16550;
86  uint8_t                 ucTrash;
87  uint8_t                 ucDataByte;
88  uint32_t                ulBaudDivisor;
89  ns16550_context        *pns16550Context;
90  setRegister_f           setReg;
91  getRegister_f           getReg;
92
93  pns16550Context=(ns16550_context *)malloc(sizeof(ns16550_context));
94
95  Console_Port_Data[minor].pDeviceContext=(void *)pns16550Context;
96  pns16550Context->ucModemCtrl=SP_MODEM_IRQ;
97
98  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
99  setReg   = Console_Port_Tbl[minor].setRegister;
100  getReg   = Console_Port_Tbl[minor].getRegister;
101
102  /* Clear the divisor latch, clear all interrupt enables,
103   * and reset and
104   * disable the FIFO's.
105   */
106
107  (*setReg)(pNS16550, NS16550_LINE_CONTROL, 0x0);
108  ns16550_enable_interrupts(minor, NS16550_DISABLE_ALL_INTR);
109
110  /* Set the divisor latch and set the baud rate. */
111
112  ulBaudDivisor = NS16550_Baud(
113    (uint32_t) Console_Port_Tbl[minor].ulClock,
114    (uint32_t) Console_Port_Tbl[minor].pDeviceParams
115  );
116  ucDataByte = SP_LINE_DLAB;
117  (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
118
119  /* XXX */
120  (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, ulBaudDivisor&0xff);
121  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (ulBaudDivisor>>8)&0xff);
122
123  /* Clear the divisor latch and set the character size to eight bits */
124  /* with one stop bit and no parity checking. */
125  ucDataByte = EIGHT_BITS;
126  (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
127
128  /* Enable and reset transmit and receive FIFOs. TJA     */
129  ucDataByte = SP_FIFO_ENABLE;
130  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
131
132  ucDataByte = SP_FIFO_ENABLE | SP_FIFO_RXRST | SP_FIFO_TXRST;
133  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
134
135  ns16550_enable_interrupts(minor, NS16550_DISABLE_ALL_INTR);
136
137  /* Set data terminal ready. */
138  /* And open interrupt tristate line */
139  (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);
140
141  ucTrash = (*getReg)(pNS16550, NS16550_LINE_STATUS );
142  ucTrash = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER );
143}
144
145/*
146 *  ns16550_open
147 */
148
149NS16550_STATIC int ns16550_open(
150  int      major,
151  int      minor,
152  void    * arg
153)
154{
155  /*
156   * Assert DTR
157   */
158
159  if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_DTRCTS) {
160    ns16550_assert_DTR(minor);
161  }
162
163  return(RTEMS_SUCCESSFUL);
164}
165
166/*
167 *  ns16550_close
168 */
169
170NS16550_STATIC int ns16550_close(
171  int      major,
172  int      minor,
173  void    * arg
174)
175{
176  /*
177   * Negate DTR
178   */
179  if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_DTRCTS) {
180    ns16550_negate_DTR(minor);
181  }
182
183  return(RTEMS_SUCCESSFUL);
184}
185
186/*
187 *  ns16550_write_polled
188 */
189
190NS16550_STATIC void ns16550_write_polled(
191  int   minor,
192  char  cChar
193)
194{
195  uint32_t                pNS16550;
196  unsigned char           ucLineStatus;
197  int                     iTimeout;
198  getRegister_f           getReg;
199  setRegister_f           setReg;
200
201  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
202  getReg   = Console_Port_Tbl[minor].getRegister;
203  setReg   = Console_Port_Tbl[minor].setRegister;
204
205  /*
206   * wait for transmitter holding register to be empty
207   */
208  iTimeout=1000;
209  ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
210  while ((ucLineStatus & SP_LSR_THOLD) == 0) {
211    /*
212     * Yield while we wait
213     */
214#if 0
215     if(_System_state_Is_up(_System_state_Get())) {
216       rtems_task_wake_after(RTEMS_YIELD_PROCESSOR);
217     }
218#endif
219     ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
220     if(!--iTimeout) {
221       break;
222     }
223  }
224
225  /*
226   * transmit character
227   */
228  (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, cChar);
229}
230
231/*
232 * These routines provide control of the RTS and DTR lines
233 */
234
235/*
236 *  ns16550_assert_RTS
237 */
238
239NS16550_STATIC int ns16550_assert_RTS(int minor)
240{
241  uint32_t                pNS16550;
242  uint32_t                Irql;
243  ns16550_context        *pns16550Context;
244  setRegister_f           setReg;
245
246  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
247
248  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
249  setReg   = Console_Port_Tbl[minor].setRegister;
250
251  /*
252   * Assert RTS
253   */
254  rtems_interrupt_disable(Irql);
255  pns16550Context->ucModemCtrl|=SP_MODEM_RTS;
256  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
257  rtems_interrupt_enable(Irql);
258  return 0;
259}
260
261/*
262 *  ns16550_negate_RTS
263 */
264
265NS16550_STATIC int ns16550_negate_RTS(int minor)
266{
267  uint32_t                pNS16550;
268  uint32_t                Irql;
269  ns16550_context        *pns16550Context;
270  setRegister_f           setReg;
271
272  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
273
274  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
275  setReg   = Console_Port_Tbl[minor].setRegister;
276
277  /*
278   * Negate RTS
279   */
280  rtems_interrupt_disable(Irql);
281  pns16550Context->ucModemCtrl&=~SP_MODEM_RTS;
282  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
283  rtems_interrupt_enable(Irql);
284  return 0;
285}
286
287/*
288 * These flow control routines utilise a connection from the local DTR
289 * line to the remote CTS line
290 */
291
292/*
293 *  ns16550_assert_DTR
294 */
295
296NS16550_STATIC int ns16550_assert_DTR(int minor)
297{
298  uint32_t                pNS16550;
299  uint32_t                Irql;
300  ns16550_context        *pns16550Context;
301  setRegister_f           setReg;
302
303  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
304
305  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
306  setReg   = Console_Port_Tbl[minor].setRegister;
307
308  /*
309   * Assert DTR
310   */
311  rtems_interrupt_disable(Irql);
312  pns16550Context->ucModemCtrl|=SP_MODEM_DTR;
313  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
314  rtems_interrupt_enable(Irql);
315  return 0;
316}
317
318/*
319 *  ns16550_negate_DTR
320 */
321
322NS16550_STATIC int ns16550_negate_DTR(int minor)
323{
324  uint32_t                pNS16550;
325  uint32_t                Irql;
326  ns16550_context        *pns16550Context;
327  setRegister_f           setReg;
328
329  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
330
331  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
332  setReg   = Console_Port_Tbl[minor].setRegister;
333
334  /*
335   * Negate DTR
336   */
337  rtems_interrupt_disable(Irql);
338  pns16550Context->ucModemCtrl&=~SP_MODEM_DTR;
339  (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);
340  rtems_interrupt_enable(Irql);
341  return 0;
342}
343
344/*
345 *  ns16550_set_attributes
346 *
347 *  This function sets the channel to reflect the requested termios
348 *  port settings.
349 */
350
351NS16550_STATIC int ns16550_set_attributes(
352  int                   minor,
353  const struct termios *t
354)
355{
356  uint32_t                pNS16550;
357  uint32_t                ulBaudDivisor;
358  uint8_t                 ucLineControl;
359  uint32_t                baud_requested;
360  setRegister_f           setReg;
361  getRegister_f           getReg;
362  uint32_t                Irql;
363
364  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
365  setReg   = Console_Port_Tbl[minor].setRegister;
366  getReg   = Console_Port_Tbl[minor].getRegister;
367
368  /*
369   *  Calculate the baud rate divisor
370   */
371
372  baud_requested = t->c_cflag & CBAUD;
373  if (!baud_requested)
374    baud_requested = B9600;              /* default to 9600 baud */
375
376  ulBaudDivisor = NS16550_Baud(
377    (uint32_t) Console_Port_Tbl[minor].ulClock,
378    termios_baud_to_number(baud_requested)
379  );
380
381  ucLineControl = 0;
382
383  /*
384   *  Parity
385   */
386
387  if (t->c_cflag & PARENB) {
388    ucLineControl |= SP_LINE_PAR;
389    if (!(t->c_cflag & PARODD))
390      ucLineControl |= SP_LINE_ODD;
391  }
392
393  /*
394   *  Character Size
395   */
396
397  if (t->c_cflag & CSIZE) {
398    switch (t->c_cflag & CSIZE) {
399      case CS5:  ucLineControl |= FIVE_BITS;  break;
400      case CS6:  ucLineControl |= SIX_BITS;   break;
401      case CS7:  ucLineControl |= SEVEN_BITS; break;
402      case CS8:  ucLineControl |= EIGHT_BITS; break;
403    }
404  } else {
405    ucLineControl |= EIGHT_BITS;               /* default to 9600,8,N,1 */
406  }
407
408  /*
409   *  Stop Bits
410   */
411
412  if (t->c_cflag & CSTOPB) {
413    ucLineControl |= SP_LINE_STOP;              /* 2 stop bits */
414  } else {
415    ;                                           /* 1 stop bit */
416  }
417
418  /*
419   *  Now actually set the chip
420   */
421
422  rtems_interrupt_disable(Irql);
423
424    /*
425     *  Set the baud rate
426     */
427
428    (*setReg)(pNS16550, NS16550_LINE_CONTROL, SP_LINE_DLAB);
429    /* XXX are these registers right? */
430    (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, ulBaudDivisor&0xff);
431    (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (ulBaudDivisor>>8)&0xff);
432
433    /*
434     *  Now write the line control
435     */
436    (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucLineControl );
437
438  rtems_interrupt_enable(Irql);
439
440  return 0;
441}
442
443/*
444 *  ns16550_process
445 *
446 *  This routine is the console interrupt handler for A port.
447 */
448
449NS16550_STATIC void ns16550_process(
450        int             minor
451)
452{
453  uint32_t                pNS16550;
454  volatile uint8_t        ucLineStatus;
455  volatile uint8_t        ucInterruptId;
456  unsigned char           cChar;
457  getRegister_f           getReg;
458  setRegister_f           setReg;
459
460  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
461  getReg   = Console_Port_Tbl[minor].getRegister;
462  setReg   = Console_Port_Tbl[minor].setRegister;
463
464  do {
465    /*
466     * Deal with any received characters
467     */
468    while(TRUE) {
469      ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
470      if(~ucLineStatus & SP_LSR_RDY) {
471        break;
472      }
473      cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
474      rtems_termios_enqueue_raw_characters(
475        Console_Port_Data[minor].termios_data,
476        &cChar,
477        1
478      );
479    }
480
481    /*
482     *  TX all the characters we can
483     */
484
485    while(TRUE) {
486        ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
487        if(~ucLineStatus & SP_LSR_THOLD) {
488          /*
489           * We'll get another interrupt when
490           * the transmitter holding reg. becomes
491           * free again
492           */
493          break;
494        }
495
496#if 0
497        /* XXX flow control not completely supported in libchip */
498
499        if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
500          ns16550_negate_RTS(minor);
501        }
502#endif
503
504    rtems_termios_dequeue_characters(Console_Port_Data[minor].termios_data, 1);
505    if (rtems_termios_dequeue_characters(
506         Console_Port_Data[minor].termios_data, 1)) {
507        if (Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
508          ns16550_negate_RTS(minor);
509        }
510        Console_Port_Data[minor].bActive = FALSE;
511        ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
512        break;
513      }
514
515      ucInterruptId = (*getReg)(pNS16550, NS16550_INTERRUPT_ID);
516    }
517  } while((ucInterruptId&0xf)!=0x1);
518}
519
520#if defined(__PPC__)
521#ifdef _OLD_EXCEPTIONS
522
523/*
524 *  ns16550_isr
525 */
526
527NS16550_STATIC rtems_isr ns16550_isr(
528  rtems_vector_number vector
529)
530{
531  int     minor;
532
533  for(minor=0;minor<Console_Port_Count;minor++) {
534    if(Console_Port_Tbl[minor].ulIntVector == vector &&
535       Console_Port_Tbl[minor].deviceType == SERIAL_NS16550 ) {
536      ns16550_process(minor);
537    }
538  }
539}
540
541#else
542
543NS16550_STATIC rtems_isr ns16550_isr(
544  void *entry
545)
546{
547  console_tbl *ptr = entry;
548  int         minor;
549
550  for(minor=0;minor<Console_Port_Count;minor++) {
551    if( &Console_Port_Tbl[minor] == ptr ) {
552      ns16550_process(minor);
553    }
554  }
555
556}
557
558#endif
559#endif
560
561/*
562 *  ns16550_enable_interrupts
563 *
564 *  This routine initializes the port to have the specified interrupts masked.
565 */
566
567NS16550_STATIC void ns16550_enable_interrupts(
568  int minor,
569  int mask
570)
571{
572  uint32_t       pNS16550;
573  setRegister_f  setReg;
574
575  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
576  setReg   = Console_Port_Tbl[minor].setRegister;
577
578  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, mask);
579}
580
581/*
582 *  ns16550_initialize_interrupts
583 *
584 *  This routine initializes the port to operate in interrupt driver mode.
585 */
586
587#if defined(__PPC__)
588#ifdef _OLD_EXCEPTIONS
589NS16550_STATIC void ns16550_initialize_interrupts(int minor)
590{
591  ns16550_init(minor);
592
593  Console_Port_Data[minor].bActive = FALSE;
594
595  set_vector(ns16550_isr, Console_Port_Tbl[minor].ulIntVector, 1);
596 
597  ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR);
598}
599#else
600
601static void null_fun(){}
602
603NS16550_STATIC void ns16550_initialize_interrupts(int minor)
604{
605#ifdef BSP_SHARED_HANDLER_SUPPORT
606  rtems_irq_connect_data IrqData = {0,
607                                    ns16550_isr,
608                                    &Console_Port_Data[minor],
609                                    (rtems_irq_enable)null_fun,
610                                    (rtems_irq_disable)null_fun,
611                                    (rtems_irq_is_enabled)null_fun,
612                                    NULL
613                                   };
614#else
615  rtems_irq_connect_data IrqData = {0,
616                                    ns16550_isr,
617                                    &Console_Port_Data[minor],
618                                    (rtems_irq_enable)null_fun,
619                                    (rtems_irq_disable)null_fun,
620                                    (rtems_irq_is_enabled)null_fun
621                                   };
622#endif
623
624  ns16550_init(minor);
625
626  Console_Port_Data[minor].bActive = FALSE;
627
628  IrqData.name  = (rtems_irq_symbolic_name)(Console_Port_Tbl[minor].ulIntVector );
629
630#ifdef BSP_SHARED_HANDLER_SUPPORT
631  if (!BSP_install_rtems_shared_irq_handler (&IrqData)) {
632#else
633  if (!BSP_install_rtems_irq_handler(&IrqData)) {
634#endif
635    printk("Error installing interrupt handler!\n");
636    rtems_fatal_error_occurred(1);
637  }
638
639  ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR);
640}
641
642#endif
643#endif
644
645/*
646 *  ns16550_write_support_int
647 *
648 *  Console Termios output entry point.
649 */
650
651NS16550_STATIC int ns16550_write_support_int(
652  int   minor,
653  const char *buf,
654  int   len
655)
656{
657  uint32_t       Irql;
658  uint32_t       pNS16550;
659  setRegister_f  setReg;
660
661  setReg   = Console_Port_Tbl[minor].setRegister;
662  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
663
664  /*
665   *  We are using interrupt driven output and termios only sends us
666   *  one character at a time.
667   */
668
669  if ( !len )
670    return 0;
671
672  if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
673    ns16550_assert_RTS(minor);
674  }
675
676  rtems_interrupt_disable(Irql);
677    if ( Console_Port_Data[minor].bActive == FALSE) {
678      Console_Port_Data[minor].bActive = TRUE;
679      ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR);
680    }
681    (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, *buf);
682  rtems_interrupt_enable(Irql);
683
684  return 1;
685}
686
687/*
688 *  ns16550_write_support_polled
689 *
690 *  Console Termios output entry point.
691 *
692 */
693
694NS16550_STATIC int ns16550_write_support_polled(
695  int         minor,
696  const char *buf,
697  int         len
698)
699{
700  int nwrite = 0;
701
702  /*
703   * poll each byte in the string out of the port.
704   */
705  while (nwrite < len) {
706    /*
707     * transmit character
708     */
709    ns16550_write_polled(minor, *buf++);
710    nwrite++;
711  }
712
713  /*
714   * return the number of bytes written.
715   */
716  return nwrite;
717}
718
719/*
720 *  ns16550_inbyte_nonblocking_polled
721 *
722 *  Console Termios polling input entry point.
723 */
724
725NS16550_STATIC int ns16550_inbyte_nonblocking_polled(
726  int minor
727)
728{
729  uint32_t             pNS16550;
730  unsigned char        ucLineStatus;
731  char                 cChar;
732  getRegister_f        getReg;
733
734  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
735  getReg   = Console_Port_Tbl[minor].getRegister;
736
737  ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
738  if(ucLineStatus & SP_LSR_RDY) {
739    cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
740    return (int)cChar;
741  } else {
742    return -1;
743  }
744}
Note: See TracBrowser for help on using the repository browser.