add serial port

This commit is contained in:
rick 2023-09-14 22:47:59 +02:00
parent 84c5da93a7
commit e4db0a38d8
Signed by: Rick
GPG key ID: 5CBE8779CD27BCBA
3 changed files with 184 additions and 1 deletions

View file

@ -1,4 +1,4 @@
OBJECTS = loader.o framebuffer.o io.o kmain.o
OBJECTS = loader.o serial.o framebuffer.o io.o kmain.o
CC = gcc
CFLAGS = -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector \

94
include/serial.h Normal file
View file

@ -0,0 +1,94 @@
/** @file serial.h
* Fichier d'en-tête pour les ports séries.
*
* Références:
* - documentation sur les ports séries :
* - https://wiki.osdev.org/Serial_Ports
* - https://devse.wiki/x86_64/p%C3%A9riph%C3%A9riques/com
* - https://www.sci.muni.cz/docs/pc/serport.txt
* - `LCR (Line Control Register)`
* - `FCR (FIFO Control Register)`
* - `Excursion: Why and how to use the FIFOs (by Scott C. Sadow)`
*/
#ifndef SERIAL_H
#define SERIAL_H
#define COM1_PORT 0x03F8
#define COM_DATA(base) (base)
#define COM_INTERRUPT(base) (base + 1)
#define COM_FIFO(base) (base + 2)
#define COM_LINE_CONTROL(base) (base + 3)
#define COM_MODEM_CONTROL(base) (base + 4)
#define COM_LINE_STATUS(base) (base + 5)
#define COM_MODEM_STATUS(base) (base + 6)
#define COM_SCRATCH(base) (base + 7)
#define DLAB_BYTE 0x80
/**
* Fonction pour définir la vitesse de transmission du port série.
*
* L'UART (le controleur de série) a une horloge interne tournant à 115200
* ticks par seconde. On peut modifier cette vitesse en la divisant. Cela se
* fait en activant le DLAB.
*
* @param port le port série à configurer
* @param div le diviseur du nombre de tick
*/
void serial_set_baud(unsigned short port, unsigned short div);
/**
* Configuration du port série.
*
* On configure le port série via le canal de controle de ligne. Il prend un
* byte découpé en plusieurs sous parties : (7) DBPPPSTT (0).
*
* - D = bit pour activer le DLAB;
* - B = active le break (cf. section `LCR (Line Control Register)` de
* https://www.sci.muni.cz/docs/pc/serport.txt);
* - P = parité (cf. https://wiki.osdev.org/Serial_Ports#Parity);
* - S = nombre de bits de stop (0 = 1, 1 = 1.5/2);
* - T = taille de la communication (0 = 5; 3 = 8)
*
* Notre configuration étant basique, nous nous contentons d'utiliser la taille
* maximale de données, sans parité et avec un bit de stop.
*
* @param port le port à configurer
*/
void serial_config(unsigned short port);
/**
* Vérifie si la file d'envoie est vide.
*
* @param port le port à vérifier
* @return 1 si la file est vide, 0 sinon
*/
int serial_is_fifo_empty(unsigned short port);
/**
* Envoie une chaine de caractère à un port série.
*
* La fonction fait une attente active lorsque la chaine est trop grosse. À
* corriger dans le futur.
*
* @param port le port envoyer la chaine. Il doit être configuré avant.
* @param buf la chaine de caractère
* @param len la longueur de la chaine
* @return la taille de la chaine envoyée
*/
unsigned int serial_write(unsigned short port, char *buf, unsigned int len);
/**
* Vérifie si le port série marche bien.
*
* Configure le port avec un diviseur de 5 et active le drapeau de boucle du
* modem.
*
* @param port le port tester la configuration
* @return 0 si tout est OK, un chiffre positif sinon
*/
int serial_test_configuration(unsigned short port);
#endif

89
src/serial.c Normal file
View file

@ -0,0 +1,89 @@
#include "io.h"
void serial_set_baud(unsigned short port, unsigned short div)
{
outb(COM_LINE_CONTROL(port), DLAB_BYTE);
outb(COM_DATA(port), (div >> 8) & 0x00FF);
outb(COM_INTERRUPT(port), div & 0x00FF);
outb(COM_LINE_CONTROL(port), 0);
}
void serial_config(unsigned short port)
{
/* configuration de la façon d'envoyer des données. Ici, on envoie des
caractères de 8 bits, avec 1 bit de stop et aucune parité. */
outb(COM_LINE_CONTROL(port), 0x03);
/* configuration de la file FIFO. Lorsqu'on essaie d'envoyer trop de
* caractères et que la vitesse du port ne suit pas, les caractères en trop
* sont stockés dans cette file. Ici, on vide la file de réception et
* d'envoie, on stocke 14 bytes dans les files et on l'active. cf. FCR (FIFO
* Control Register) : https://www.sci.muni.cz/docs/pc/serport.txt
*/
outb(COM_FIFO(port), 0xC7);
/* configuration du modem, on active la réception et l'envoie de données */
outb(COM_MODEM_CONTROL(port), 0x03);
}
int serial_is_fifo_empty(unsigned short port)
{
return inb(COM_LINE_STATUS(port)) & 0x20;
}
unsigned int serial_write(unsigned short port, char *buf, unsigned int len)
{
unsigned int ret;
char tmp;
ret = 0;
while (ret < len)
{
tmp = *(buf + ret);
/* pas ouf, à trouver un meilleur moyen d'attendre lorsque la file est
* pleine */
while (!serial_is_fifo_empty(port));
outb(COM_DATA(port), tmp);
ret++;
}
return ret;
}
int serial_test_configuration(unsigned short port)
{
char *tmp;
char t;
int i, ret;
serial_set_baud(port, 5);
serial_config(port);
/* activation du retour pour pouvoir tester */
outb(COM_MODEM_CONTROL(port), 0x13);
i = 0;
tmp = "coucou";
do
{
outb(COM_DATA(port), *(tmp + i));
i++;
}
while (*(tmp + i) != '\0');
ret = 0;
i = 0;
do
{
t = inb(COM_DATA(port));
if (t != *(tmp + i)) ret++;
i++;
}
while (t != '\0');
return ret;
}