Keyboard interrupt handler for own kernel (C) - gcc

I am writing a tiny OS as part of an assigment for school,but I got stuck when it comes to get keyboard input (press a key -> display it on screen). I am using the Bare Bones tutorial from osdev.org (gcc cross-compiler, GRUB bootloader, ld linker) and since I am in protected mode I can not use BIOS interrupts for input, that's why I have to write my own interrupt handler (?) but I'm not sure how to do that even after I read some osdev articles and forum discussions. Very similar problem (http://forum.osdev.org/viewtopic.php?f=1&t=9746) except that I don't know how to "set up the interrupts".
#if !defined(__cplusplus)
#include <stdbool.h> /* C doesn't have booleans by default. */
#endif
#include <stddef.h>
#include <stdint.h>
#define INT_DISABLE 0
#define INT_ENABLE 0x200
#define PIC1 0x20
#define PIC2 0xA0
#define ICW1 0x11
#define ICW4 0x01
void outb( unsigned short port, unsigned char val )
{
asm volatile("outb %0, %1" : : "a"(val), "Nd"(port) );
}
static __inline unsigned char inb (unsigned short int port)
{
unsigned char _v;
__asm__ __volatile__ ("inb %w1,%0":"=a" (_v):"Nd" (port));
return _v;
}
void init_pics(int pic1, int pic2)
{
/* send ICW1 */
outb(PIC1, ICW1);
outb(PIC2, ICW1);
/* send ICW2 */
outb(PIC1 + 1, pic1);
outb(PIC2 + 1, pic2);
/* send ICW3 */
outb(PIC1 + 1, 4);
outb(PIC2 + 1, 2);
/* send ICW4 */
outb(PIC1 + 1, ICW4);
outb(PIC2 + 1, ICW4);
/* disable all IRQs */
outb(PIC1 + 1, 0xFF);
}
/*irrelevant code*/
#if defined(__cplusplus)
extern "C" /* Use C linkage for kernel_main. */
#endif
void kernel_main()
{
terminal_initialize();
char c;
init_pics(0x20, 0x28);
c = inb(0x60);
terminal_putchar(c);
}
This is printing me a white box.If I try listening to port 0x64 I get some different character. I don't expect this to work, because I don't have the interrupt. I think it should be something like
void _interrupt button_pressed()
{
/*code*/
}
if(button_pressed)
{
c = inb(0x60);
//code to translate the char to ASCII
terminal_putchar(asciiChar);
}
Any help is appreciated. Thanks

If there is someone interested how I solved the problem, here is the solution
char c = 0;
init_pics(0x20, 0x28);
do
{
if(inb(0x60)!=c) //PORT FROM WHICH WE READ
{
c = inb(0x60);
if(c>0)
{
terminal_putinput(c); //print on screen
}
}
}
while(c!=1); // 1= ESCAPE
c variable contains the code of the pressed button. Creating a translation array by associating to each code, the corresponding ASCII code, I can print the letter/number which is written on button.
The buttons code can be found here: http://www.nondot.org/sabre/os/files/HCI/keyboard.txt
The ASCII here: http://www.ascii-code.com/

Related

Replace USB keyboard key press through a kernel module

I need to replace a key press from a USB keyboard. As a first, I am just trying to print the scan code of the pressed key. However, my current implementation prints Scan Code 7e for every key press or even mouse movements. I used IRQ 19, which corresponds to my USB keyboard. I don't see any resources regarding USB keyboard interception. I would appreciate any pointers to get the below code to work.
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h> /* Needed for the macros */
#include <linux/interrupt.h>
#include <asm/io.h>
#include <linux/irq.h>
#include <linux/irqdesc.h>
#define KBD_IRQ 19 /* IRQ number for keyboard (i8042) */
#define KBD_DATA_REG 0x60 /* I/O port for keyboard data */
#define KBD_SCANCODE_MASK 0x7f
#define KBD_STATUS_MASK 0x80
static irqreturn_t kbd2_isr(int irq, void *dev_id)
{
char scancode;
scancode = inb(KBD_DATA_REG);
printk("Scan Code %x %s\n",
scancode & KBD_SCANCODE_MASK,
scancode & KBD_STATUS_MASK ? "Released" : "Pressed");
return IRQ_HANDLED;
}
static int __init kbd2_init(void)
{
int retval_irqreg;
retval_irqreg= request_irq(KBD_IRQ, (irq_handler_t) kbd2_isr, IRQF_SHARED, "kbd2", (void *)kbd2_isr);
return 0;
}
static void __exit kbd2_exit(void)
{ printk(KERN_INFO "removed interceptKBD module \n");
free_irq(KBD_IRQ, (void *)kbd2_isr);
}
module_init(kbd2_init);
module_exit(kbd2_exit);
MODULE_LICENSE("GPL");

AVR Atmega32a USART library isn't working

/*
RC_Car_AVR.c
Created: 4/18/2018 7:55:07 PM
Author :
*/
#define F_CPU 16000000
#define BAUD 9600
#define TUBRR (((F_CPU / 16) / BAUD) - 1)
#include <avr/io.h>
#include <util/delay.h>
char Read;
void USART_Init(void){
UBRRL = TUBRR;
UCSRB = (1<<TXEN)|(1<<RXEN);
UCSRC = (1<<UCSZ1)|(1<<UCSZ0);
}
char USART_Receive(void){
/* Wait for data to be received */
while (!(UCSRA & (1<<RXC)));
/* Get and return received data from buffer */
return UDR;
}
int main(void){
USART_Init();
DDRB |= (1<<0);
PORTB |= (1<<0);
while (1){
Read = USART_Receive();
if(Read == 'F'){
PORTB ^= (1<<0);
_delay_ms(100);
}
}
}
I'm trying to toggle an LED when I receive a certain character through the Bluetooth module (HC05).
I've written the USART library just like the datasheet but it doesn't seem to work (I'm only concerned with the initialization and receiving code since I'm working on a half duplex system so i don't need the transmition part).
I'm using Atmega32a with a 16MHz external crystal Oscillator.
Please tell me if you find anything wrong.
Thanks in advance.
Your Initialization is wrong.
Try this
void USART_Init(void){
UBRRL = TUBRR;
UBRRH = TUBRR >> 8;
UCSRB = (1<<TXEN)|(1<<RXEN);
UCSRC = (1<<UCSZ1)|(3<<UCSZ0);
}
This is the following initialization code provided in data sheet of atmega32
void USART_Init( unsigned int baud )
{
/* Set baud rate */
UBRRH = (unsigned char)(baud>>8);
UBRRL = (unsigned char)baud;
/* Enable receiver and transmitter */
UCSRB = (1<<RXEN)|(1<<TXEN);
/* Set frame format: 8data, 2stop bit */
UCSRC = (1<<URSEL)|(1<<USBS)|(3<<UCSZ0);
}
I know following datasheet is little overhead in beginning But eventually you will see all your answers are provided there.

setting an i2c register to high

I have this project that my boss asked me to do and the first step is to figure out how to set a given I2C register to high or low using the silicon lab library, if anyone knows any good sources for this type of problem please provide them thank you. The pic that I am using is the pic16f1823, I've already looked at the documentation of the pic but into only states how to read and write to an I2c.
I use this as a header file and seems to work well for PIC16F1827 which is basically the same as the 1823. It used the peripheral of the PIC. Just include in in any c file you want to use i2c in. Make sure you #define FOSC in order to calculate the correct baud rate. Also double check the port and tris assignments are correct for your device and make adjustments.
It uses polling instead of an interrupt. Uncomment the interrupt setup code and write an interrupt service routine to catch the interrupts.
#ifndef I2C_H
#define I2C_H
#ifdef __cplusplus
extern "C" {
#endif
/*
* Hi-Tech C I2C library for 12F1822
* Master mode routines for I2C MSSP port to read and write to slave device
* Copyright (C)2011 HobbyTronics.co.uk 2011
* Freely distributable.
*/
#define I2C_WRITE 0
#define I2C_READ 1
// Initialise MSSP port. (12F1822 - other devices may differ)
void i2c_Init(void){
// Initialise I2C MSSP
// Master 100KHz
TRISB2 = 1;
TRISB5 = 1;
SSP1CON1 = 0b00101000; // I2C Master mode
SSP1CON2 = 0b00000000;
SSP1CON3 = 0b00000000;
//SSP1MSK = 0b00000000;
SSP1ADD = I2C_BRG; // clock = FOSC/(4 * (SSPxADD+1))
//SSP1IE = 1; // enable interrupt
SSP1STAT = 0b10000000;
}
// i2c_Wait - wait for I2C transfer to finish
void i2c_Wait(void){
while ( ( SSP1CON2 & 0x1F ) || ( SSPSTAT & 0x04 ) );
}
// i2c_Start - Start I2C communication
void i2c_Start(void)
{
i2c_Wait();
SSP1CON2bits.SEN=1;
}
// i2c_Restart - Re-Start I2C communication
void i2c_Restart(void){
i2c_Wait();
SSP1CON2bits.RSEN=1;
}
// i2c_Stop - Stop I2C communication
void i2c_Stop(void)
{
i2c_Wait();
SSP1CON2bits.PEN=1;
}
// i2c_Write - Sends one byte of data
void i2c_Write(unsigned char data)
{
i2c_Wait();
SSPBUF = data;
}
// i2c_Address - Sends Slave Address and Read/Write mode
// mode is either I2C_WRITE or I2C_READ
void i2c_Address(unsigned char address, unsigned char mode)
{
unsigned char l_address;
l_address=address<<1;
l_address+=mode;
i2c_Wait();
SSPBUF = l_address;
}
// i2c_Read - Reads a byte from Slave device
unsigned char i2c_Read(unsigned char ack)
{
// Read data from slave
// ack should be 1 if there is going to be more data read
// ack should be 0 if this is the last byte of data read
unsigned char i2cReadData;
i2c_Wait();
SSP1CON2bits.RCEN=1;
i2c_Wait();
i2cReadData = SSPBUF;
i2c_Wait();
if ( ack ) SSP1CON2bits.ACKDT=0; // Ack
else SSP1CON2bits.ACKDT=1; // NAck
SSP1CON2bits.ACKEN=1; // send acknowledge sequence
return( i2cReadData );
}
#ifdef __cplusplus
}
#endif
#endif /* I2C_H */
Then you can use the higher level functions defined above to control a device, which is described in the datasheet of the slave device.
For example, to read from an eeprom:
#include <xc.h>
#define FOSC 16000000
#include "i2c.h"
unsigned char i2c_read_eeprom( unsigned char slaveaddress, unsigned char memaddress )
{
unsigned char data;
data = 123;
i2c_Start();
i2c_Address( slaveaddress, I2C_WRITE);
i2c_Write(memaddress);
if( SSP1CON2bits.ACKSTAT )
txstring("ACK!\r\n");
else txstring("nACK!\r\n");
i2c_Start();
i2c_Address( slaveaddress, I2C_READ);
data = i2c_Read(0);
i2c_Stop();
return data;
}

text mode cursor doesn't appear in qemu vga emulator

I have a problem with the function that updates cursor position in text mode
the function definition and declaration are
#include <sys/io.h>
signed int VGAx = 0,VGAy=0;
void setcursor()
{
uint16_t position = VGAx+VGAy*COLS;
outb(0x0f, 0x03d4);
outb((position<<8)>>8,0x03d5);
outb(0x0e,0x03d4);
outb(position>>8,0x03d5);
}
and the file sys/io.h
static inline unsigned char inb (unsigned short int port)
{
unsigned char value;
asm ("inb %0, %%al":"=rm"(value):"a"(port));
return value;
}
static inline void outb(unsigned char value, unsigned short int port)
{
asm volatile ("outb %%al, $0"::"rm"(value), "a"(port));
}
before using the function the cursor sometimes was blinking underscore and sometimes didn't appear while after using the function no cursor appeared
here is the main function that runs
#include <vga/vga.h>
int kmain(){
setcursor()
setbgcolor(BLACK);
clc();
setforecolor(BLUE);
terminal_write('h');
setcursor();
return 0;
}
I tried using this function
void enable_cursor() {
outb(0x3D4, 0x0A);
char curstart = inb(0x3D5) & 0x1F; // get cursor scanline start
outb(0x3D4, 0x0A);
outb(0x3D5, curstart | 0x20); // set enable bit
}
which is provided here but I got this error
inline asm: operand type mismatch for 'in'
any help is appreciated
EDIT
I tried to fix the wrong inb and outb:
static inline unsigned char inb (unsigned short int port)
{
unsigned char value;
asm volatile("inb %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
static inline void outb(unsigned char value, unsigned short int port)
{
asm volatile ("outb %%al, $0"::"Nd"(value), "a"(port));
}
I guess this is the right definition but still no cursor appeard
EDIT 2
I followed the given answer and defined the io.h file as the following
static inline unsigned char inb (unsigned short int port)
{
unsigned char value;
asm volatile ("inb %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
static inline void outb(unsigned char value, unsigned short int port)
{
asm volatile ("outb %0, %1"::"a"(value), "Nd"(port));
}
I would like to mention that I also addedenable_cursor(); to the beginning of kmain now the compile time error is fixed but no cursor appeared (which is the main problem)
EDIT 3
I would like to point out that a version of the whole code is availabe on gihub if any one want access to pieces of code that are no available in the question
inb and outb Function Bugs
This code for inb is incorrect:
static inline unsigned char inb (unsigned short int port)
{
unsigned char value;
asm ("inb %0, %%al":"=rm"(value):"a"(port));
return value;
}
A few problems with it:
It seems you have the parameters to inb reversed. See the instruction set reference for inb. Remember that in AT&T syntax (that you are using in your GNU Assembler code) the operands are reversed. The instruction set reference shows them in Intel format.
The port number is either specified as an immediate 8 bit value or passed in the DX register. The proper constraint for specifying the DX register or an immediate 8 bit value for inb/outb is Nd. See my Stackoverflow answer here for an explanation of the constraint Nd.
The destination that the value read is returned in is either AL/AX/EAX so a constraint =rm on the output that says an available register or memory address is incorrect. It should be =a in your case.
Your code should be something like:
static inline unsigned char inb (unsigned short int port)
{
unsigned char value;
asm volatile ("inb %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
Your assembler template for outb is incorrect:
static inline void outb(unsigned char value, unsigned short int port)
{
asm volatile ("outb %%al, $0"::"rm"(value), "a"(port));
}
A couple problems with it:
The port number is either specified as an immediate 8 bit value or passed in the DX register. The proper constraint for specifying the DX register or an immediate 8 bit value for inb/outb is Nd. See my Stackoverflow answer here for an explanation of the constraint Nd.
The value to output on the port has to be specified in AL/AX/EAX so a constraint rm on the value that says an available register or memory address is incorrect. It should be a in your case. See the instruction set reference for outb
The code should probably look something like:
static inline void outb(unsigned char value, unsigned short int port)
{
asm volatile ("outb %0, %1"::"a"(value), "Nd"(port));
}
Enabling and Disabling the Cursor
I had to look up the VGA registers about the cursor and found this document on the cursor start register which says:
Cursor Start Register (Index 0Ah)
-------------------------------------------------
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
-------------------------------------------------
| | | CD | Cursor Scan Line Start |
-------------------------------------------------
CD -- Cursor Disable
This field controls whether or not the text-mode cursor is displayed. Values are:
0 -- Cursor Enabled
1 -- Cursor Disabled
Cursor Scan Line Start
An important thing is that the cursor is disabled when the bit 5 is set. In your github setcursor function you do this:
outb(curstart | 0x20, 0x3D5);
curstart | 0x20 sets bit 5 (0x20 = 0b00100000). If you want to clear bit 5 and enable the cursor, then you bitwise NEGATE(~) the bitmask and bitwise AND (&) that with curstart. It should look like this:
outb(curstart & ~0x20, 0x3D5);
VGA Function Bugs
Once you have the cursor properly enabled it will render the cursor in the foreground color (attribute) for the particular video location it is currently over. One thing I noticed is that your clc routine does this:
vga_deref_80x24(VGAx,VGAy) = \
vga_encode_80x24(' ',BgColor,BgColor);
The thing to observe is that you set the attribute for the foreground and background colors to BgColor . If you set the bgcolor to black before calling clc it will flash a black underline cursor on a black background rendering it invisible on any screen location. For the cursor to be visible it must be on a screen location where the foreground and background are different colors. One way to see if this works is to change the code to:
vga_deref_80x24(VGAx,VGAy) = \
vga_encode_80x24(' ',BgColor,ForeColor);
I think it is a bug that you are clearing it with encoding vga_encode_80x24(' ',BgColor,BgColor); I think you mean to use vga_encode_80x24(' ',BgColor,ForeColor);
Now in your kmain function you need to set a ForeColor and BgColor before calling clc and they both must be different color to make the cursor visible. You have this code:
setbgcolor(BLACK);
clc();
setforecolor(BLUE);
It should now be:
setbgcolor(BLACK);
setforecolor(BLUE);
clc();
Now if the cursor is rendered anywhere on an unwritten location on the screen it will flash BLUE underline on BLACK background.
This should solve your cursor problem. However, I noticed that you also use encode vga_encode_80x24(' ',BgColor,BgColor); in your VGA scrolldown and terminal_control functions. I think this is a bug as well, and I think you should use encode vga_encode_80x24(' ',BgColor,ForeColor); instead. You do seem to set it properly in terminal_write.
If you want to change the color of the cursor at any location you could write a function that changes the foreground attribute under the cursor location without changing the background color. Make sure the two attributes (Foreground and background color) are different for the cursor to be visible. If you wish to hide the cursor you can set foreground and background color the same color for the screen location the cursor is currently at.
The problem is in your outb code. Also be aware of order port and value parameters.
Following works for me:
static inline unsigned char inb (unsigned short int port)
{
unsigned char value;
asm volatile ("inb %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
static inline void outb (unsigned short int port, unsigned char value)
{
asm volatile ("outb %b0,%w1": :"a" (value), "Nd" (port));
}
void update_cursor(int x, int y)
{
uint16_t pos = y * 80 + x;
outb(0x3D4, 0x0F);
outb(0x3D5, (uint8_t) (pos & 0xFF));
outb(0x3D4, 0x0E);
outb(0x3D5, (uint8_t) ((pos >> 8) & 0xFF));
}

PDCURSES assignign value to integer make error

I'm using PDCURSES on Windows 7 and my program stops working at the beginning.
I noticed the error is made by assigning value to integer in line: 41 .
Unfortunately I have no idea why.
I'm writing in C in Code blocks.
I would be grateful if someone could help me.
Error message
#include <curses.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#define ILOSC_NAZW 5
#define false 0
#define true 1
WINDOW * createwin(int height,int width,int starty,int startx);
void uaktualnij(WINDOW*left,char *nazwa);
void wyswietl_nazwy(WINDOW *right,char **losowe_nazwy);
int poprawnie=0,blednie=0;
int main()
{
char *losowe_nazwy[ILOSC_NAZW]={"JACK","TOM","KEN","JESSY","ANDRIEJ"};
char str[20];
int c,startx,starty,height,width;
WINDOW *right,*left,*console;
initscr();
refresh();
startx=0,starty=0,height=LINES/2,width=COLS/2;
left=createwin(height,width,starty,startx);
startx=COLS/2;
right=createwin(height,width,starty,startx);
startx=0;
starty=LINES/2;
width=COLS;
console=createwin(height,width,starty,startx);
getmaxyx(right,starty,startx);
uaktualnij(right,NULL);
mvwprintw(left,starty/2-starty/4,startx/2-strlen("PODAJ NAZWE UZYTKOWNIKA")/2,"PODAJ NAZWE UZYTKOWNIKA");
wrefresh(left);
wyswietl_nazwy(left,losowe_nazwy);
c=0; // assigning value make error
getmaxyx(console,starty,startx);
wmove(console,starty-2,2);
memset(str,0,sizeof *str);
wclear(console);
wborder(console,0,0,0,0,0,0,0,0);
wrefresh(console);
getch();
endwin();
return 0;
}
WINDOW * createwin(int height,int width,int starty,int startx){
WINDOW *mywin=newwin(height,width,starty,startx);
box(mywin,0,0);
wrefresh(mywin);
return mywin;
}
void czekaj(double sec){
clock_t koniec=clock()+sec*CLOCKS_PER_SEC;
while(clock()<koniec)continue;
}
void wyswietl_nazwy(WINDOW *right,char **losowe_nazwy){
int i=ILOSC_NAZW;
int rstartx,rstarty;
getmaxyx(right,rstarty,rstartx);
while( i>=0)
{
mvwprintw(right,rstarty/2-rstarty/4+2,rstartx/2-strlen(losowe_nazwy[i])/2,"%s",losowe_nazwy[i]);
wrefresh(right);
czekaj(0.2);
wmove(right,rstarty/2-rstarty/4+2,rstartx/2-strlen(losowe_nazwy[i]));
wclrtoeol(right);
mvwaddch(right,rstarty/2-rstarty/4+2,rstartx-1,ACS_VLINE);
wrefresh(right);
i--;
}
}
void uaktualnij(WINDOW*left,char *nazwa){
int startx,starty;
wclear(left);
wborder(left,0,0,0,0,0,0,0,0);
getyx(left,starty,startx);
mvwprintw(left,starty+1,startx+1,"Poprawnie: %i",poprawnie);
mvwprintw(left,starty+2,startx+1,"Blednie: %i",blednie);
if(nazwa!=NULL)
mvwprintw(left,starty+3,startx+1,"Wproawadzona nazwa uzytkowanika: %s",nazwa);
wrefresh(left);
}
Since PDCurses is written in C, I am assuming that you are compiling in C.
Move the
int c;
to just below the line
char *losowe_nazwy[ILOSC_NAZW]={"JACK","TOM","KEN","JESSY","ANDRIEJ"};
int c;
The C language expects those sorts of definitions to be at the top of the function.

Resources