Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
P
Pintos_forked_resit
Manage
Activity
Members
Plan
Wiki
Code
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Deploy
Releases
Package registry
Model registry
Operate
Terraform modules
Analyze
Contributor analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
s2-alsaloumi
Pintos_forked_resit
Commits
1816a6d7
Commit
1816a6d7
authored
1 year ago
by
h2-addad
Browse files
Options
Downloads
Patches
Plain Diff
Update
parent
235ef1b6
No related branches found
No related tags found
No related merge requests found
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
src/devices/serial.c
+228
-0
228 additions, 0 deletions
src/devices/serial.c
with
228 additions
and
0 deletions
src/devices/serial.c
0 → 100644
+
228
−
0
View file @
1816a6d7
#include
"devices/serial.h"
#include
<debug.h>
#include
"devices/input.h"
#include
"devices/intq.h"
#include
"devices/timer.h"
#include
"threads/io.h"
#include
"threads/interrupt.h"
#include
"threads/synch.h"
#include
"threads/thread.h"
/* Register definitions for the 16550A UART used in PCs.
The 16550A has a lot more going on than shown here, but this
is all we need.
Refer to [PC16650D] for hardware information. */
/* I/O port base address for the first serial port. */
#define IO_BASE 0x3f8
/* DLAB=0 registers. */
#define RBR_REG (IO_BASE + 0)
/* Receiver Buffer Reg. (read-only). */
#define THR_REG (IO_BASE + 0)
/* Transmitter Holding Reg. (write-only). */
#define IER_REG (IO_BASE + 1)
/* Interrupt Enable Reg.. */
/* DLAB=1 registers. */
#define LS_REG (IO_BASE + 0)
/* Divisor Latch (LSB). */
#define MS_REG (IO_BASE + 1)
/* Divisor Latch (MSB). */
/* DLAB-insensitive registers. */
#define IIR_REG (IO_BASE + 2)
/* Interrupt Identification Reg. (read-only) */
#define FCR_REG (IO_BASE + 2)
/* FIFO Control Reg. (write-only). */
#define LCR_REG (IO_BASE + 3)
/* Line Control Register. */
#define MCR_REG (IO_BASE + 4)
/* MODEM Control Register. */
#define LSR_REG (IO_BASE + 5)
/* Line Status Register (read-only). */
/* Interrupt Enable Register bits. */
#define IER_RECV 0x01
/* Interrupt when data received. */
#define IER_XMIT 0x02
/* Interrupt when transmit finishes. */
/* Line Control Register bits. */
#define LCR_N81 0x03
/* No parity, 8 data bits, 1 stop bit. */
#define LCR_DLAB 0x80
/* Divisor Latch Access Bit (DLAB). */
/* MODEM Control Register. */
#define MCR_OUT2 0x08
/* Output line 2. */
/* Line Status Register. */
#define LSR_DR 0x01
/* Data Ready: received data byte is in RBR. */
#define LSR_THRE 0x20
/* THR Empty. */
/* Transmission mode. */
static
enum
{
UNINIT
,
POLL
,
QUEUE
}
mode
;
/* Data to be transmitted. */
static
struct
intq
txq
;
static
void
set_serial
(
int
bps
);
static
void
putc_poll
(
uint8_t
);
static
void
write_ier
(
void
);
static
intr_handler_func
serial_interrupt
;
/* Initializes the serial port device for polling mode.
Polling mode busy-waits for the serial port to become free
before writing to it. It's slow, but until interrupts have
been initialized it's all we can do. */
static
void
init_poll
(
void
)
{
ASSERT
(
mode
==
UNINIT
);
outb
(
IER_REG
,
0
);
/* Turn off all interrupts. */
outb
(
FCR_REG
,
0
);
/* Disable FIFO. */
set_serial
(
9600
);
/* 9.6 kbps, N-8-1. */
outb
(
MCR_REG
,
MCR_OUT2
);
/* Required to enable interrupts. */
intq_init
(
&
txq
);
mode
=
POLL
;
}
/* Initializes the serial port device for queued interrupt-driven
I/O. With interrupt-driven I/O we don't waste CPU time
waiting for the serial device to become ready. */
void
serial_init_queue
(
void
)
{
enum
intr_level
old_level
;
if
(
mode
==
UNINIT
)
init_poll
();
ASSERT
(
mode
==
POLL
);
intr_register_ext
(
0x20
+
4
,
serial_interrupt
,
"serial"
);
mode
=
QUEUE
;
old_level
=
intr_disable
();
write_ier
();
intr_set_level
(
old_level
);
}
/* Sends BYTE to the serial port. */
void
serial_putc
(
uint8_t
byte
)
{
enum
intr_level
old_level
=
intr_disable
();
if
(
mode
!=
QUEUE
)
{
/* If we're not set up for interrupt-driven I/O yet,
use dumb polling to transmit a byte. */
if
(
mode
==
UNINIT
)
init_poll
();
putc_poll
(
byte
);
}
else
{
/* Otherwise, queue a byte and update the interrupt enable
register. */
if
(
old_level
==
INTR_OFF
&&
intq_full
(
&
txq
))
{
/* Interrupts are off and the transmit queue is full.
If we wanted to wait for the queue to empty,
we'd have to reenable interrupts.
That's impolite, so we'll send a character via
polling instead. */
putc_poll
(
intq_getc
(
&
txq
));
}
intq_putc
(
&
txq
,
byte
);
write_ier
();
}
intr_set_level
(
old_level
);
}
/* Flushes anything in the serial buffer out the port in polling
mode. */
void
serial_flush
(
void
)
{
enum
intr_level
old_level
=
intr_disable
();
while
(
!
intq_empty
(
&
txq
))
putc_poll
(
intq_getc
(
&
txq
));
intr_set_level
(
old_level
);
}
/* The fullness of the input buffer may have changed. Reassess
whether we should block receive interrupts.
Called by the input buffer routines when characters are added
to or removed from the buffer. */
void
serial_notify
(
void
)
{
ASSERT
(
intr_get_level
()
==
INTR_OFF
);
if
(
mode
==
QUEUE
)
write_ier
();
}
/* Configures the serial port for BPS bits per second. */
static
void
set_serial
(
int
bps
)
{
int
base_rate
=
1843200
/
16
;
/* Base rate of 16550A, in Hz. */
uint16_t
divisor
=
base_rate
/
bps
;
/* Clock rate divisor. */
ASSERT
(
bps
>=
300
&&
bps
<=
115200
);
/* Enable DLAB. */
outb
(
LCR_REG
,
LCR_N81
|
LCR_DLAB
);
/* Set data rate. */
outb
(
LS_REG
,
divisor
&
0xff
);
outb
(
MS_REG
,
divisor
>>
8
);
/* Reset DLAB. */
outb
(
LCR_REG
,
LCR_N81
);
}
/* Update interrupt enable register. */
static
void
write_ier
(
void
)
{
uint8_t
ier
=
0
;
ASSERT
(
intr_get_level
()
==
INTR_OFF
);
/* Enable transmit interrupt if we have any characters to
transmit. */
if
(
!
intq_empty
(
&
txq
))
ier
|=
IER_XMIT
;
/* Enable receive interrupt if we have room to store any
characters we receive. */
if
(
!
input_full
())
ier
|=
IER_RECV
;
outb
(
IER_REG
,
ier
);
}
/* Polls the serial port until it's ready,
and then transmits BYTE. */
static
void
putc_poll
(
uint8_t
byte
)
{
ASSERT
(
intr_get_level
()
==
INTR_OFF
);
while
((
inb
(
LSR_REG
)
&
LSR_THRE
)
==
0
)
continue
;
outb
(
THR_REG
,
byte
);
}
/* Serial interrupt handler. */
static
void
serial_interrupt
(
struct
intr_frame
*
f
UNUSED
)
{
/* Inquire about interrupt in UART. Without this, we can
occasionally miss an interrupt running under QEMU. */
inb
(
IIR_REG
);
/* As long as we have room to receive a byte, and the hardware
has a byte for us, receive a byte. */
while
(
!
input_full
()
&&
(
inb
(
LSR_REG
)
&
LSR_DR
)
!=
0
)
input_putc
(
inb
(
RBR_REG
));
/* As long as we have a byte to transmit, and the hardware is
ready to accept a byte for transmission, transmit a byte. */
while
(
!
intq_empty
(
&
txq
)
&&
(
inb
(
LSR_REG
)
&
LSR_THRE
)
!=
0
)
outb
(
THR_REG
,
intq_getc
(
&
txq
));
/* Update interrupt enable register based on queue status. */
write_ier
();
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment