source: rtems/c/src/lib/libbsp/m68k/gen68340/console/console.c @ 6693a68

4.104.114.84.95
Last change on this file since 6693a68 was 03c0961, checked in by Joel Sherrill <joel.sherrill@…>, on 07/24/98 at 16:18:39

Patch from David Fiddes <D.J.Fiddes@…>:

Here's a small patch I forgot about from earlier in the week that removes
the hack fix Geoffroy had to use to get his BSP to work properly.

The termios osend() fix took care of this.

  • Property mode set to 100644
File size: 19.8 KB
Line 
1/*
2 *  68340/68349 console serial I/O.
3 *
4 *  Author:
5 *  Geoffroy Montel
6 *  France Telecom - CNET/DSM/TAM/CAT
7 *  4, rue du Clos Courtel
8 *  35512 CESSON-SEVIGNE
9 *  FRANCE
10 *
11 *  e-mail: g_montel@yahoo.com
12 *
13 *  COPYRIGHT (c) 1989-1998.
14 *  On-Line Applications Research Corporation (OAR).
15 *  Copyright assigned to U.S. Government, 1994.
16 *
17 *  The license and distribution terms for this file may be
18 *  found in the file LICENSE in this distribution or at
19 *
20 *  http://www.OARcorp.com/rtems/license.html.
21 *
22 *  $Id$
23 */
24
25#include <termios.h>
26#include <bsp.h>
27#include <rtems/libio.h>
28#include <m68340.h>
29#include <m340uart.h>
30#include <m340timer.h>
31
32#include <stdarg.h>
33#include <stdio.h>
34#include <stdlib.h>
35
36#define CONSOLE_VECTOR 121
37#define CONSOLE_IRQ_LEVEL 3
38#define CONSOLE_INTERRUPT_ARBITRATION 2
39
40static void *ttypA;             /* to remember which tty has been opened on channel A
41                                   used when interrupts are enabled */
42
43static void *ttypB;             /* to remember which tty has been opened on channel B
44                                   used when interrupts are enabled */
45
46unsigned char DUIER_mirror = 0 ;  /* reflects the state of IER register, which is Write Only */
47unsigned char Error_Status_A = 0; /* error status on Channel A */
48unsigned char Error_Status_B = 0; /* error status on Channel A */
49
50/*
51 * Device-specific routines
52 */
53
54#define USE_INTERRUPTS_A (m340_uart_config[UART_CHANNEL_A].mode==UART_INTERRUPTS)
55#define USE_INTERRUPTS_B (m340_uart_config[UART_CHANNEL_B].mode==UART_INTERRUPTS)
56#define CHANNEL_ENABLED_A m340_uart_config[UART_CHANNEL_A].enable
57#define CHANNEL_ENABLED_B m340_uart_config[UART_CHANNEL_B].enable
58
59#define set_DUIER(a) DUIER_mirror |= (a); DUIER = DUIER_mirror
60#define unset_DUIER(a) DUIER_mirror &= ~(a); DUIER = DUIER_mirror
61
62#define Enable_Interrupts_Tx_A if (USE_INTERRUPTS_A) set_DUIER(m340_TxRDYA)
63#define Disable_Interrupts_Tx_A if (USE_INTERRUPTS_A) unset_DUIER(m340_TxRDYA)
64
65#define Enable_Interrupts_Tx_B if (USE_INTERRUPTS_B) set_DUIER(m340_TxRDYB)
66#define Disable_Interrupts_Tx_B if (USE_INTERRUPTS_B) unset_DUIER(m340_TxRDYB)
67
68/******************************************************
69  Name: InterruptHandler
70  Input parameters: vector number
71  Output parameters: -
72  Description: UART ISR Routine, called by _RTEMS_ISR
73 *****************************************************/
74rtems_isr
75InterruptHandler (rtems_vector_number v)
76{
77 char ch;
78
79 /*****************************************************************************
80 **                             CHANNEL A                                    **
81 *****************************************************************************/
82
83    /* check Received Break*/
84    if (DUSRA & m340_RB) {
85       Error_Status_A |= m340_RB;
86       /* reset error status */
87       DUCRA = m340_Reset_Error_Status;
88    }
89
90    /* buffer received ? */
91    if (DUSRA & m340_Rx_RDY) {
92       do {
93           /* error encountered? */
94           if (DUSRA & (m340_OE | m340_PE | m340_FE | m340_RB)) {
95              Error_Status_A |= DUSRA;
96              /* reset error status */
97              DUCRA = m340_Reset_Error_Status;
98              /* all the characters in the queue may not be good */
99              while (DUSRA & m340_Rx_RDY)
100                    /* push them in a trash */
101                    ch = DURBA;
102           }
103           else {
104                 /* this is necessary, otherwise it blocks when FIFO is full */
105                 ch = DURBA;
106                 rtems_termios_enqueue_raw_characters(ttypA,&ch,1);
107           }
108       } while (DUSRA & m340_Rx_RDY);
109       Restart_Fifo_Full_A_Timer();     /* only if necessary (pointer to a fake function if
110                                           not in FIFO full mode) */
111    }
112
113    else /* if no character has been received */
114       Restart_Check_A_Timer();         /* same remark */
115
116    /* ready to accept a character ? */
117    if (DUISR & DUIER_mirror & m340_TxRDYA) {
118        Disable_Interrupts_Tx_A;
119        /* one character has been transmitted */
120        rtems_termios_dequeue_characters(ttypA,1);
121    }
122
123 /*****************************************************************************
124 **                             CHANNEL B                                    **
125 *****************************************************************************/
126
127    /* check Received Break*/
128    if (DUSRB & m340_RB) {
129       Error_Status_B |= m340_RB;
130       /* reset error status */
131       DUCRB = m340_Reset_Error_Status;
132    }
133
134    /* buffer received ? */
135    if (DUSRB & m340_Rx_RDY) {
136       do {
137           if (DUSRB & (m340_OE | m340_PE | m340_FE | m340_RB)) {
138              Error_Status_B |= DUSRB;
139              /* reset error status */
140              DUCRB = m340_Reset_Error_Status;
141              /* all the characters in the queue may not be good */
142              while (DUSRB & m340_Rx_RDY)
143                    /* push them in a trash */
144                    ch = DURBB;
145           }
146           else {
147                 ch = DURBB;
148                 rtems_termios_enqueue_raw_characters(ttypB,&ch,1);
149           }
150
151       } while (DUSRB & m340_Rx_RDY);
152       Restart_Fifo_Full_B_Timer();
153    }
154    else /* if no character has been received */
155       Restart_Check_B_Timer();
156
157    /* ready to accept a character ? */
158    if (DUISR & DUIER_mirror & m340_TxRDYB) {
159        Disable_Interrupts_Tx_B;
160        /* one character has been transmitted */
161        rtems_termios_dequeue_characters(ttypB,1);
162    }
163}
164
165/******************************************************
166  Name: InterruptWrite
167  Input parameters: minor = channel, pointer to buffer,
168                    and length of buffer to transmit
169  Output parameters: -
170  Description: write the first character of buf only
171               may be called by either console_write
172               or rtems_termios_enqueue_raw_characters
173 *****************************************************/
174static int
175InterruptWrite (int minor, const char *buf, int len)
176{
177 if (minor==UART_CHANNEL_A) {
178    if (len>0) (char)DUTBA=*buf;
179    Enable_Interrupts_Tx_A;
180 }
181 else if (minor==UART_CHANNEL_B) {
182    if (len>0) (char)DUTBB=*buf;
183    Enable_Interrupts_Tx_B;
184 }
185 return 0;
186}
187
188/******************************************************
189  Name: dbug_out_char
190  Input parameters: channel, character to emit
191  Output parameters: -
192  Description: wait for the UART to be ready to emit
193               a character and send it
194 *****************************************************/
195void dbug_out_char( int minor, int ch )
196{
197 if (minor==UART_CHANNEL_A) {
198    while (!(DUSRA & m340_Tx_RDY)) continue;
199    DUTBA=ch;
200 }
201 else if (minor==UART_CHANNEL_B) {
202    while (!(DUSRB & m340_Tx_RDY)) continue;
203    DUTBB=ch;
204 }
205}
206
207/******************************************************
208  Name: dbug_in_char
209  Input parameters: -
210  Output parameters: received character
211  Description: return the character in the UART
212 *****************************************************/
213int dbug_in_char( int minor )
214{
215 if (minor==UART_CHANNEL_A) {
216    return DURBA;
217 }
218 else if (minor==UART_CHANNEL_B) {
219    return DURBB;
220 }
221 return 0;
222}
223
224/******************************************************
225  Name: dbug_char_present
226  Input parameters: channel #
227  Output parameters: TRUE or FALSE
228  Description: return whether there's a character
229               in the receive buffer
230 *****************************************************/
231int dbug_char_present( int minor )
232{
233 if (minor==UART_CHANNEL_A) {
234    return (DUSRA & m340_Rx_RDY);
235 }
236 else if (minor==UART_CHANNEL_B) {
237    return (DUSRB & m340_Rx_RDY);
238 }
239 return 0;
240}
241
242/******************************************************
243  Name: dbugInitialise
244  Input parameters: -
245  Output parameters: -
246  Description: Init the UART
247 *****************************************************/
248static void
249dbugInitialise ()
250{
251     t_baud_speed_table uart_config;            /* configuration of UARTS */
252
253     /*
254      * Reset Receiver
255      */
256     DUCRA = m340_Reset_Receiver;
257     DUCRB = m340_Reset_Receiver;
258
259     /*
260      * Reset Transmitter
261      */
262     DUCRA = m340_Reset_Transmitter;
263     DUCRB = m340_Reset_Transmitter;
264
265     /*
266      * Enable serial module for normal operation, ignore FREEZE, select the crystal clock,
267      * supervisor/user serial registers unrestricted
268      * interrupt arbitration at priority CONSOLE_INTERRUPT_ARBITRATION
269      * WARNING : 8 bits access only on this UART!
270      */
271     DUMCRH = 0x00;
272     DUMCRL = CONSOLE_INTERRUPT_ARBITRATION;
273
274     /*
275      * Interrupt level register
276      */
277     DUILR = CONSOLE_IRQ_LEVEL;
278
279     /* sets the IVR */
280     DUIVR = CONSOLE_VECTOR;
281
282     /* search for a correct m340 uart configuration */
283     uart_config = Find_Right_m340_UART_Config(m340_uart_config[UART_CHANNEL_A].rx_baudrate,
284                                               m340_uart_config[UART_CHANNEL_A].tx_baudrate,
285                                               CHANNEL_ENABLED_A,
286                                               m340_uart_config[UART_CHANNEL_B].rx_baudrate,
287                                               m340_uart_config[UART_CHANNEL_B].tx_baudrate,
288                                               CHANNEL_ENABLED_B);
289
290     /*****************************************************************************
291     **                         CHANNEL A                                        **
292     *****************************************************************************/
293     if (CHANNEL_ENABLED_A) {
294
295        if (USE_INTERRUPTS_A) {
296           rtems_isr_entry old_handler;
297           rtems_status_code sc;
298
299           extern void _Debug_ISR_Handler_Console(void);
300
301           sc = rtems_interrupt_catch (InterruptHandler,
302                                       CONSOLE_VECTOR,
303                                       &old_handler);
304
305           /* uncomment this if you want to pass control to your own ISR handler
306              it may be usefull to do so to check for performances with an oscilloscope */
307           /*
308           {
309            proc_ptr ignored;
310            _CPU_ISR_install_raw_handler( CONSOLE_VECTOR, _Debug_ISR_Handler_Console, &ignored );
311           }
312           */
313
314           /*
315            * Interrupt Enable Register
316            * Enable Interrupts on Channel A Receiver Ready
317            */
318           set_DUIER(m340_RxRDYA);
319        }
320        else {
321                /*
322                 * Disable Interrupts on channel A
323                 */
324                unset_DUIER(m340_RxRDYA&m340_TxRDYA);
325        }
326       
327        /*
328         * Change set of baud speeds
329         * disable input control
330         */
331        /* no good uart configuration ? */
332        if (uart_config.nb<1) rtems_fatal_error_occurred (-1);
333       
334        if (uart_config.baud_speed_table[UART_CHANNEL_A].set==1)
335           DUACR = m340_BRG_Set1;
336        else
337           DUACR = m340_BRG_Set2;
338
339        /*
340         * make OPCR an auxiliary function serving the communication channels
341         */
342        DUOPCR = m340_OPCR_Aux;
343
344        /* poll the XTAL_RDY bit until it is cleared to ensure that an unstable crystal
345           input is not applied to the baud rate generator */
346        while (DUISR & m340_XTAL_RDY) continue;
347
348        /*
349         * Serial Channel Baud Speed
350         */
351        DUCSRA = (uart_config.baud_speed_table[UART_CHANNEL_A].rcs << 4)
352               | (uart_config.baud_speed_table[UART_CHANNEL_A].tcs);
353
354        /*
355         * Serial Channel Configuration
356         */
357        DUMR1A = m340_uart_config[UART_CHANNEL_A].parity_mode
358               | m340_uart_config[UART_CHANNEL_A].bits_per_char
359               | m340_RxRTS;
360
361        if (m340_uart_config[UART_CHANNEL_A].rx_mode==UART_FIFO_FULL) DUMR1A |= m340_R_F | m340_ERR;
362
363        /*
364         * Serial Channel Configuration 2
365         */
366        DUMR2A |= m340_normal;
367
368        /*
369         * Enable Channel A: transmitter and receiver
370         */
371        DUCRA = m340_Transmitter_Enable | m340_Receiver_Enable;
372     } /* channel A enabled */
373
374     /*****************************************************************************
375     **                         CHANNEL B                                        **
376     *****************************************************************************/
377     if (CHANNEL_ENABLED_B) {
378
379        /* we mustn't set the console vector twice! */
380        if ((USE_INTERRUPTS_B && !(CHANNEL_ENABLED_A))
381           || (USE_INTERRUPTS_B && CHANNEL_ENABLED_A && !USE_INTERRUPTS_A)) {
382           rtems_isr_entry old_handler;
383           rtems_status_code sc;
384
385           extern void _Debug_ISR_Handler_Console(void);
386
387           sc = rtems_interrupt_catch (InterruptHandler,
388                                       CONSOLE_VECTOR,
389                                       &old_handler);
390
391           /* uncomment this if you want to pass control to your own ISR handler
392              it may be usefull to do so to check for performances with an oscilloscope */
393           /*
394           {
395            proc_ptr ignored;
396            _CPU_ISR_install_raw_handler( CONSOLE_VECTOR, _Debug_ISR_Handler_Console, &ignored );
397           }
398           */
399
400           /*
401            * Interrupt Enable Register
402            * Enable Interrupts on Channel A Receiver Ready
403            */
404           set_DUIER(m340_RxRDYB);
405        }
406        else {
407                /*
408                 * Disable Interrupts on channel B
409                 */
410                unset_DUIER(m340_RxRDYB&m340_TxRDYB);
411        }
412       
413        /*
414         * Change set of baud speeds
415         * disable input control
416         */
417
418        /* no good uart configuration ? */
419        if (uart_config.nb<2) rtems_fatal_error_occurred (-1);
420       
421        /* don't set DUACR twice! */
422        if (!CHANNEL_ENABLED_A)
423           if (uart_config.baud_speed_table[UART_CHANNEL_B].set==1) DUACR = m340_BRG_Set1;
424           else DUACR = m340_BRG_Set2;
425
426        /*
427         * make OPCR an auxiliary function serving the communication channels
428         */
429        if (!CHANNEL_ENABLED_A) DUOPCR = m340_OPCR_Aux;
430
431        /* poll the XTAL_RDY bit until it is cleared to ensure that an unstable crystal
432           input is not applied to the baud rate generator */
433        while (DUISR & m340_XTAL_RDY) continue;
434
435        /*
436         * Serial Channel Baud Speed
437         */
438        DUCSRB = (uart_config.baud_speed_table[UART_CHANNEL_B].rcs << 4)
439               | (uart_config.baud_speed_table[UART_CHANNEL_B].tcs);
440
441        /*
442         * Serial Channel Configuration
443         */
444        DUMR1B = m340_uart_config[UART_CHANNEL_B].parity_mode
445               | m340_uart_config[UART_CHANNEL_B].bits_per_char
446               | m340_RxRTS;
447
448        if (m340_uart_config[UART_CHANNEL_B].rx_mode==UART_FIFO_FULL) DUMR1B |= m340_R_F | m340_ERR;
449
450        /*
451         * Serial Channel Configuration 2
452         */
453        DUMR2B |= m340_normal;
454
455        /*
456         * Enable Channel A: transmitter and receiver
457         */
458        DUCRB = m340_Transmitter_Enable | m340_Receiver_Enable;
459     } /* channel B enabled */
460}
461
462/******************************************************
463  Name: SetAttributes
464  Input parameters: termios structure, channel
465  Output parameters: -
466  Description: return whether there's a character
467               in the receive buffer
468  TO DO: add the channel # to check for!!
469 *****************************************************/
470static int
471SetAttributes (int minor, const struct termios *t)
472{
473 rtems_interrupt_level level;
474 float ispeed, ospeed;
475 int isp, osp;
476
477 /* output speed */
478 if (t->c_cflag & CBAUDEX)
479    osp = (t->c_cflag & CBAUD) + CBAUD + 1;
480 else
481    osp = t->c_cflag & CBAUD;
482
483 /* input speed */
484 isp = (t->c_cflag / (CIBAUD / CBAUD)) &  CBAUD;
485
486 /* convert it */
487 ispeed = termios_baud_rates_equivalence(isp);
488 ospeed = termios_baud_rates_equivalence(osp);
489
490 if (ispeed || ospeed) {
491       /* update config table */
492       m340_uart_config[UART_CHANNEL_A].rx_baudrate = ((minor==UART_CHANNEL_A)&&(ispeed!=0)) ? ispeed  : m340_uart_config[UART_CHANNEL_A].rx_baudrate;
493       m340_uart_config[UART_CHANNEL_A].tx_baudrate = ((minor==UART_CHANNEL_A)&&(ospeed!=0)) ? ospeed  : m340_uart_config[UART_CHANNEL_A].tx_baudrate;
494       m340_uart_config[UART_CHANNEL_B].rx_baudrate = ((minor==UART_CHANNEL_B)&&(ispeed!=0)) ? ispeed  : m340_uart_config[UART_CHANNEL_B].rx_baudrate;
495       m340_uart_config[UART_CHANNEL_B].tx_baudrate = ((minor==UART_CHANNEL_B)&&(ospeed!=0)) ? ospeed  : m340_uart_config[UART_CHANNEL_B].tx_baudrate;
496 }
497
498 /* change parity */
499 if (t->c_cflag & PARENB) {
500    if (t->c_cflag & PARODD) m340_uart_config[minor].parity_mode = m340_Odd_Parity;
501    else m340_uart_config[minor].parity_mode = m340_Even_Parity;
502 }
503
504 /* change bits per character */
505 if (t->c_cflag & CSIZE) {
506    switch (t->c_cflag & CSIZE) {
507        default:        break;
508        case CS5:       m340_uart_config[minor].bits_per_char = m340_5bpc;      break;
509        case CS6:       m340_uart_config[minor].bits_per_char = m340_6bpc;      break;
510        case CS7:       m340_uart_config[minor].bits_per_char = m340_7bpc;      break;
511        case CS8:       m340_uart_config[minor].bits_per_char = m340_8bpc;      break;
512    }
513 }
514
515 /* if serial module configuration has been changed */
516 if (t->c_cflag & (CBAUD | CIBAUD | CSIZE | PARENB)) {
517    rtems_interrupt_disable(level);
518    /* reinit the UART */
519    dbugInitialise();
520    rtems_interrupt_enable (level);
521 }
522
523 return 0;
524}
525
526/******************************************************
527  Name: console_reserve_resources
528  Input parameters: -
529  Output parameters: -
530  Description: Reserve resources consumed by this driver
531 *****************************************************/
532void console_reserve_resources(
533  rtems_configuration_table *configuration
534)
535{
536        rtems_termios_reserve_resources (configuration, 1);
537}
538
539/******************************************************
540  Name: console_initialize
541  Input parameters: MAJOR # of console_driver,
542                    minor is always 0,
543                    args are always NULL
544  Output parameters: -
545  Description: Reserve resources consumed by this driver
546  TODO: We should pass m340_uart_config table in arg
547 *****************************************************/
548rtems_device_driver console_initialize(
549  rtems_device_major_number  major,
550  rtems_device_minor_number  minor,
551  void                      *arg
552)
553{
554        rtems_status_code status;
555        int i;
556
557        /*
558         * Set up TERMIOS
559         */
560        rtems_termios_initialize ();
561
562        /*
563         * Do device-specific initialization
564         */
565        Init_UART_Table();
566        dbugInitialise ();
567        Fifo_Full_Timer_initialize();
568
569        /*
570         * Register the devices
571         */
572        for (i=0; i<UART_NUMBER_OF_CHANNELS; i++) {
573            if (m340_uart_config[i].enable) {
574                status = rtems_io_register_name (m340_uart_config[i].name, major, i);
575                if (status != RTEMS_SUCCESSFUL)
576                        rtems_fatal_error_occurred (status);
577            }
578        }
579
580        return RTEMS_SUCCESSFUL;
581}
582
583/******************************************************
584  Name: console_open
585  Input parameters: channel #, arg
586  Output parameters: -
587  Description: open the device
588 *****************************************************/
589rtems_device_driver console_open(
590  rtems_device_major_number major,
591  rtems_device_minor_number minor,
592  void                    * arg
593)
594{
595 rtems_status_code sc = 0;
596
597 static const rtems_termios_callbacks intrCallbacks = {
598                NULL,                   /* firstOpen */
599                NULL,                   /* lastClose */
600                NULL,                   /* pollRead */
601                InterruptWrite,         /* write */
602                SetAttributes,          /* setAttributes */
603                NULL,                   /* stopRemoteTx */
604                NULL,                   /* startRemoteTx */
605                1                       /* outputUsesInterrupts */
606 };
607
608 static const rtems_termios_callbacks pollCallbacks = {
609                NULL,                   /* firstOpen */
610                NULL,                   /* lastClose */
611                dbugRead,               /* pollRead */
612                dbugWrite,              /* write */
613                SetAttributes,          /* setAttributes */
614                NULL,                   /* stopRemoteTx */
615                NULL,                   /* startRemoteTx */
616                0                       /* outputUsesInterrupts */
617 };
618
619 if (minor==UART_CHANNEL_A) {
620        if (USE_INTERRUPTS_A) {
621                rtems_libio_open_close_args_t *args = arg;
622
623                sc |= rtems_termios_open (major, minor, arg, &intrCallbacks);
624                ttypA = args->iop->data1;
625        }
626        else {
627                sc |= rtems_termios_open (major, minor, arg, &pollCallbacks);
628        }
629 }
630
631 else if (minor==UART_CHANNEL_B) {
632        if (USE_INTERRUPTS_B) {
633                rtems_libio_open_close_args_t *args = arg;
634
635                sc |= rtems_termios_open (major, minor, arg, &intrCallbacks);
636                ttypB = args->iop->data1;
637        }
638        else {
639                sc |= rtems_termios_open (major, minor, arg, &pollCallbacks);
640        }
641 }
642
643 else return RTEMS_INVALID_NUMBER;
644
645 return sc;
646}
647 
648/******************************************************
649  Name: console_close
650  Input parameters: channel #, termios args
651  Output parameters: -
652  Description: close the device
653 *****************************************************/
654rtems_device_driver console_close(
655  rtems_device_major_number major,
656  rtems_device_minor_number minor,
657  void                    * arg
658)
659{
660        return rtems_termios_close (arg);
661}
662
663/******************************************************
664  Name: console_read
665  Input parameters: channel #, termios args
666  Output parameters: -
667  Description: read the device
668 *****************************************************/
669rtems_device_driver console_read(
670  rtems_device_major_number major,
671  rtems_device_minor_number minor,
672  void                    * arg
673)
674{
675        return rtems_termios_read (arg);
676}
677
678/******************************************************
679  Name: console_write
680  Input parameters: channel #, termios args
681  Output parameters: -
682  Description: write to the device
683 *****************************************************/
684rtems_device_driver console_write(
685  rtems_device_major_number major,
686  rtems_device_minor_number minor,
687  void                    * arg
688)
689{
690        return rtems_termios_write (arg);
691}
692
693/******************************************************
694  Name: console_control
695  Input parameters: channel #, termios args
696  Output parameters: -
697  Description: Handle ioctl request
698 *****************************************************/
699rtems_device_driver console_control(
700  rtems_device_major_number major,
701  rtems_device_minor_number minor,
702  void                    * arg
703)
704{
705        rtems_libio_ioctl_args_t *args = arg;
706 
707        if (args->command == RTEMS_IO_SET_ATTRIBUTES)
708                SetAttributes (minor, (struct termios *)args->buffer);
709 
710        return rtems_termios_ioctl (arg);
711}
Note: See TracBrowser for help on using the repository browser.