Mon 9 Dec 20:40:44 CET 2024
This commit is contained in:
		
							parent
							
								
									6460cc87d0
								
							
						
					
					
						commit
						64731d077e
					
				| 
						 | 
				
			
			@ -4,13 +4,16 @@ This software is distributed under the GNU General Public License.
 | 
			
		|||
See the file LICENSE for details.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// Derived from: https://wiki.osdev.org/Serial_Ports#Example_Code
 | 
			
		||||
// For more info:
 | 
			
		||||
//     https://wiki.osdev.org/Serial_Ports
 | 
			
		||||
//     http://www.webcitation.org/5ugQv5JOw
 | 
			
		||||
/* Derived from: https://wiki.osdev.org/Serial_Ports#Example_Code
 | 
			
		||||
  For more info:
 | 
			
		||||
     https://wiki.osdev.org/Serial_Ports
 | 
			
		||||
     http://www.webcitation.org/5ugQv5JOw
 | 
			
		||||
  TODO: Interrupt support; polling IO is not a good idea for carbon free air
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include "kernel/types.h"
 | 
			
		||||
#include "ioports.h"
 | 
			
		||||
#include "interrupt.h"
 | 
			
		||||
#include "string.h"
 | 
			
		||||
#include "device.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -33,11 +36,13 @@ See the file LICENSE for details.
 | 
			
		|||
 | 
			
		||||
#define SERIAL_FCR 2
 | 
			
		||||
#define SERIAL_FIFO_ENABLE (0x01 << 0)
 | 
			
		||||
#define SERIAL_FIFO_CLEAR_RECIEVER (0x01 << 1)
 | 
			
		||||
#define SERIAL_FIFO_CLEAR_RECEIVER (0x01 << 1)
 | 
			
		||||
#define SERIAL_FIFO_CLEAR_TRANSMITTER (0x01 << 2)
 | 
			
		||||
#define SERIAL_FIFO_DMA_MODE (0x01 << 3)
 | 
			
		||||
#define SERIAL_TRIGGER_LEVEL0 (0x01 << 6)
 | 
			
		||||
#define SERIAL_TRIGGER_LEVEL1 (0x01 << 7)
 | 
			
		||||
#define SERIAL_TRIGGER_LEVEL0 (0x00 << 6)  // 1 byte in FIFO
 | 
			
		||||
#define SERIAL_TRIGGER_LEVEL1 (0x01 << 6)  // 4 bytes
 | 
			
		||||
#define SERIAL_TRIGGER_LEVEL2 (0x10 << 6)  // 8 bytes
 | 
			
		||||
#define SERIAL_TRIGGER_LEVEL3 (0x11 << 6)  // 14 bytes
 | 
			
		||||
 | 
			
		||||
#define SERIAL_LCR 3
 | 
			
		||||
#define SERIAL_CHARLEN_START (0x01 << 0)
 | 
			
		||||
| 
						 | 
				
			
			@ -58,11 +63,21 @@ See the file LICENSE for details.
 | 
			
		|||
 | 
			
		||||
#define SERIAL_SCRATCH 7
 | 
			
		||||
 | 
			
		||||
// units 0,1,2,3
 | 
			
		||||
static const int serial_ports[4] = { COM1, COM2, COM3, COM4 };
 | 
			
		||||
static const int serial_ports_irq[4] = { 4, 3, 4, 3 };
 | 
			
		||||
 | 
			
		||||
static void serial_init_port(int port)
 | 
			
		||||
static void serial_interrupt(int i, int intr_code)
 | 
			
		||||
{
 | 
			
		||||
	//Disable iterrupts
 | 
			
		||||
  printf("serial_interrupt %d %d\n",i,intr_code);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void serial_init_port(int unit)
 | 
			
		||||
{
 | 
			
		||||
  int port = serial_ports[unit];
 | 
			
		||||
	//Disable interrupts
 | 
			
		||||
	outb(0x00, port + SERIAL_IRQ_ENABLE);
 | 
			
		||||
	//Enable DLAB(set baud rate divisor)
 | 
			
		||||
	outb(SERIAL_DLAB_ENABLE, port + SERIAL_LCR);
 | 
			
		||||
| 
						 | 
				
			
			@ -73,11 +88,20 @@ static void serial_init_port(int port)
 | 
			
		|||
	//8 bits, no parity, one stop bit
 | 
			
		||||
	outb(SERIAL_CHARLEN_START * 3, port + SERIAL_LCR);
 | 
			
		||||
	//Enable FIFO, clear them, with 14 - byte threshold
 | 
			
		||||
	outb(SERIAL_FIFO_ENABLE | SERIAL_FIFO_CLEAR_RECIEVER | SERIAL_FIFO_CLEAR_TRANSMITTER | SERIAL_TRIGGER_LEVEL0 | SERIAL_TRIGGER_LEVEL1, port + SERIAL_FCR);
 | 
			
		||||
	outb(SERIAL_FIFO_ENABLE | SERIAL_FIFO_CLEAR_RECEIVER | SERIAL_FIFO_CLEAR_TRANSMITTER | SERIAL_TRIGGER_LEVEL0 , port + SERIAL_FCR);
 | 
			
		||||
	//IRQs enabled, RTS / DSR set
 | 
			
		||||
	outb(SERIAL_DATA_TERMINAL_READY | SERIAL_REQUEST_TO_SEND | SERIAL_AUX_OUT2, port + SERIAL_MCR);
 | 
			
		||||
 | 
			
		||||
	// setup interrupt support
 | 
			
		||||
  interrupt_register(serial_ports_irq[unit], serial_interrupt);
 | 
			
		||||
  interrupt_enable(serial_ports_irq[unit]);
 | 
			
		||||
  // + received data available
 | 
			
		||||
	outb(0x01, port + SERIAL_IRQ_ENABLE); 
 | 
			
		||||
  
 | 
			
		||||
	printf("[COM%d] serial_init_port %x: ready.\n",unit,port);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int serial_received(int port)
 | 
			
		||||
{
 | 
			
		||||
	return inb(port + SERIAL_LSR) & SERIAL_DATA_AVAILABLE;
 | 
			
		||||
| 
						 | 
				
			
			@ -93,12 +117,23 @@ static int is_valid_port(uint8_t port_no)
 | 
			
		|||
	return port_no < sizeof(serial_ports) / sizeof(int);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char serial_read(uint8_t port_no)
 | 
			
		||||
int serial_read(uint8_t port_no)
 | 
			
		||||
{
 | 
			
		||||
	if(!is_valid_port(port_no))
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	while(serial_received(serial_ports[port_no]) == 0);
 | 
			
		||||
	printf("[COM%d]\n",port_no);
 | 
			
		||||
	return inb(serial_ports[port_no]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int serial_read_nonblock(uint8_t port_no)
 | 
			
		||||
{
 | 
			
		||||
	if(!is_valid_port(port_no))
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	if (serial_received(serial_ports[port_no]) == 0) return -1;
 | 
			
		||||
	printf("[COM%d]\n",port_no);
 | 
			
		||||
	return inb(serial_ports[port_no]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -115,7 +150,9 @@ int serial_write(uint8_t port_no, char a)
 | 
			
		|||
int serial_device_probe( int unit, int *blocksize, int *nblocks, char *info )
 | 
			
		||||
{
 | 
			
		||||
	if(unit<0 || unit>3) return 0;
 | 
			
		||||
	serial_init_port(serial_ports[unit]);
 | 
			
		||||
	// TODO probing for real hardware
 | 
			
		||||
	// reinit, second time (first time in serial_init, but needed to register the device)
 | 
			
		||||
	serial_init_port(unit);
 | 
			
		||||
	*blocksize = 1;
 | 
			
		||||
	*nblocks = 0;
 | 
			
		||||
	strcpy(info,"serial");
 | 
			
		||||
| 
						 | 
				
			
			@ -124,10 +161,24 @@ int serial_device_probe( int unit, int *blocksize, int *nblocks, char *info )
 | 
			
		|||
 | 
			
		||||
int serial_device_read( int unit, void *data, int length, int offset )
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	int i,byte;
 | 
			
		||||
	char *cdata = data;
 | 
			
		||||
	for(i=0;i<length;i++) {
 | 
			
		||||
		cdata[i] = serial_read(unit);
 | 
			
		||||
		byte = serial_read(unit);
 | 
			
		||||
		if (byte==-1) break;
 | 
			
		||||
		cdata[i] = (char)byte; 
 | 
			
		||||
	}
 | 
			
		||||
	return length;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int serial_device_read_nonblock( int unit, void *data, int length, int offset )
 | 
			
		||||
{
 | 
			
		||||
	int i,byte;
 | 
			
		||||
	char *cdata = data;
 | 
			
		||||
	for(i=0;i<length;i++) {
 | 
			
		||||
		byte = serial_read_nonblock(unit);
 | 
			
		||||
		if (byte==-1) break;
 | 
			
		||||
		cdata[i] = (char)byte;
 | 
			
		||||
	}
 | 
			
		||||
	return length;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -142,11 +193,12 @@ int serial_device_write( int unit, const void *data, int length, int offset )
 | 
			
		|||
	return length;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static struct device_driver serial_driver = {
 | 
			
		||||
       .name           = "serial",
 | 
			
		||||
       .probe          = serial_device_probe,
 | 
			
		||||
       .read           = serial_device_read,
 | 
			
		||||
       .read_nonblock  = serial_device_read,
 | 
			
		||||
       .read_nonblock  = serial_device_read_nonblock,
 | 
			
		||||
       .write          = serial_device_write
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -154,7 +206,7 @@ void serial_init()
 | 
			
		|||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	for(i = 0; i < sizeof(serial_ports) / sizeof(int); i++) {
 | 
			
		||||
		serial_init_port(serial_ports[i]);
 | 
			
		||||
		serial_init_port(i);
 | 
			
		||||
	}
 | 
			
		||||
	device_driver_register(&serial_driver);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user