diff --git a/src/add-ons/kernel/drivers/arch/x86/Jamfile b/src/add-ons/kernel/drivers/arch/x86/Jamfile new file mode 100644 index 0000000000..4bf13cebef --- /dev/null +++ b/src/add-ons/kernel/drivers/arch/x86/Jamfile @@ -0,0 +1,4 @@ +SubDir OBOS_TOP src add-ons kernel drivers arch x86 ; + +SubInclude OBOS_TOP src add-ons kernel drivers arch x86 keyboard ; +SubInclude OBOS_TOP src add-ons kernel drivers arch x86 ps2mouse ; diff --git a/src/add-ons/kernel/drivers/arch/x86/keyboard/Jamfile b/src/add-ons/kernel/drivers/arch/x86/keyboard/Jamfile new file mode 100644 index 0000000000..356f68d566 --- /dev/null +++ b/src/add-ons/kernel/drivers/arch/x86/keyboard/Jamfile @@ -0,0 +1,3 @@ +SubDir OBOS_TOP src add-ons kernel drivers arch x86 keyboard ; + +KernelObjects keyboard.c : -fno-pic -D_KERNEL_MODE ; diff --git a/src/add-ons/kernel/drivers/arch/x86/keyboard/keyboard.c b/src/add-ons/kernel/drivers/arch/x86/keyboard/keyboard.c new file mode 100644 index 0000000000..9c2a252ce7 --- /dev/null +++ b/src/add-ons/kernel/drivers/arch/x86/keyboard/keyboard.c @@ -0,0 +1,340 @@ +/* +** Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. +** Distributed under the terms of the NewOS License. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DEVICE_NAME "keyboard" + +#define LSHIFT 42 +#define RSHIFT 54 +#define SCRLOCK 70 +#define NUMLOCK 69 +#define CAPS 58 +#define SYSREQ 55 +#define F11 87 +#define F12 88 + +#define LED_SCROLL 1 +#define LED_NUM 2 +#define LED_CAPS 4 + +static bool shift; +static int leds; +static sem_id keyboard_sem; +static mutex keyboard_read_mutex; +static char keyboard_buf[1024]; +static unsigned int head, tail; + +// stolen from nujeffos +const char unshifted_keymap[128] = { + 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 8, '\t', + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + '\\', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +const char shifted_keymap[128] = { + 0, 27, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 8, '\t', + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', 0, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0, '\\', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', '<', '>', '?', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +const char caps_keymap[128] = { + 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 8, '\t', + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '[', ']', '\n', 0, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', '\'', '`', 0, '\\', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', '<', '>', '?', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + '\\', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static void wait_for_output(void) +{ + while(in8(0x64) & 0x2) + ; +} + +static void set_leds(void) +{ + wait_for_output(); + out8(0xed, 0x60); + wait_for_output(); + out8(leds, 0x60); +} + +static ssize_t _keyboard_read(void *_buf, size_t len) +{ + unsigned int saved_tail; + char *buf = _buf; + size_t copied_bytes = 0; + size_t copy_len; + int rc; + + if(len > sizeof(keyboard_buf) - 1) + len = sizeof(keyboard_buf) - 1; + +retry: + // block here until data is ready + rc = acquire_sem_etc(keyboard_sem, 1, B_CAN_INTERRUPT, 0); + if(rc == EINTR) { + return 0; + } + + // critical section + mutex_lock(&keyboard_read_mutex); + + saved_tail = tail; + if(head == saved_tail) { + mutex_unlock(&keyboard_read_mutex); + goto retry; + } else { + // copy out of the buffer + if(head < saved_tail) + copy_len = min(len, saved_tail - head); + else + copy_len = min(len, sizeof(keyboard_buf) - head); + memcpy(buf, &keyboard_buf[head], copy_len); + copied_bytes = copy_len; + head = (head + copy_len) % sizeof(keyboard_buf); + if(head == 0 && saved_tail > 0 && copied_bytes < len) { + // we wrapped around and have more bytes to read + // copy the first part of the buffer + copy_len = min(saved_tail, len - copied_bytes); + memcpy(&buf[len], &keyboard_buf[0], copy_len); + copied_bytes += copy_len; + head = copy_len; + } + } + if(head != saved_tail) { + // we did not empty the keyboard queue + release_sem_etc(keyboard_sem, 1, B_DO_NOT_RESCHEDULE); + } + + mutex_unlock(&keyboard_read_mutex); + + return copied_bytes; +} + +static void insert_in_buf(char c) +{ + unsigned int temp_tail = tail; + + // see if the next char will collide with the head + temp_tail++; + temp_tail %= sizeof(keyboard_buf); + if(temp_tail == head) { + // buffer overflow, ditch this char + return; + } + keyboard_buf[tail] = c; + tail = temp_tail; + release_sem_etc(keyboard_sem, 1, B_DO_NOT_RESCHEDULE); +} + +static int handle_keyboard_interrupt(void* data) +{ + unsigned char key; + int retval = INT_NO_RESCHEDULE; + + key = in8(0x60); +// dprintf("handle_keyboard_interrupt: key = 0x%x\n", key); + + if(key & 0x80) { + // keyup + if(key == LSHIFT + 0x80 || key == RSHIFT + 0x80) + shift = false; + } else { + switch(key) { + case LSHIFT: + case RSHIFT: + shift = true; + break; + case CAPS: + if(leds & LED_CAPS) + leds &= ~LED_CAPS; + else + leds |= LED_CAPS; + set_leds(); + break; + case SCRLOCK: + if(leds & LED_SCROLL) { + leds &= ~LED_SCROLL; + dbg_set_serial_debug(false); + } else { + leds |= LED_SCROLL; + dbg_set_serial_debug(true); + } + set_leds(); + break; + case NUMLOCK: + if(leds & LED_NUM) + leds &= ~LED_NUM; + else + leds |= LED_NUM; + set_leds(); + break; + case SYSREQ: + panic("Keyboard Requested Halt\n"); + break; + case F11: + dbg_set_serial_debug(dbg_get_serial_debug()?false:true); + break; + case F12: + reboot(); + break; + default: { + char ascii; + + if(shift) + ascii = shifted_keymap[key]; + else + if (leds & LED_CAPS) + ascii = caps_keymap[key]; + else + ascii = unshifted_keymap[key]; + +// dprintf("ascii = 0x%x, '%c'\n", ascii, ascii); + if(ascii != 0) { + insert_in_buf(ascii); + retval = INT_RESCHEDULE; + } else { +// dprintf("keyboard: unknown scan-code 0x%x\n",key); + } + } + } + } + return retval; +} + +static int keyboard_open(const char *name, uint32 flags, void * *cookie) +{ + *cookie = NULL; + return 0; +} + +static int keyboard_close(void * cookie) +{ + return 0; +} + +static int keyboard_freecookie(void * cookie) +{ + return 0; +} + +static ssize_t keyboard_read(void * cookie, off_t pos, void *buf, size_t *len) +{ + int rv; + if (*len < 0) + return 0; + + rv = _keyboard_read(buf, *len); + if (rv < 0) + return rv; + *len = rv; + return 0; +} + +static ssize_t keyboard_write(void * cookie, off_t pos, const void *buf, size_t *len) +{ + return EROFS; +} + +static int keyboard_ioctl(void * cookie, uint32 op, void *buf, size_t len) +{ + return EINVAL; +} + +device_hooks keyboard_hooks = { + &keyboard_open, + &keyboard_close, + &keyboard_freecookie, + &keyboard_ioctl, + &keyboard_read, + &keyboard_write, + NULL, + NULL, +// NULL, +// NULL +}; + +static int setup_keyboard(void) +{ + keyboard_sem = create_sem(0, "keyboard_sem"); + if(keyboard_sem < 0) + panic("could not create keyboard sem!\n"); + + if(mutex_init(&keyboard_read_mutex, "keyboard_read_mutex") < 0) + panic("could not create keyboard read mutex!\n"); + + shift = 0; + leds = 0; + // have the scroll lock reflect the state of serial debugging + if(dbg_get_serial_debug()) + leds |= LED_SCROLL; + set_leds(); + + head = tail = 0; + + return 0; +} + +status_t init_hardware() +{ + setup_keyboard(); + int_set_io_interrupt_handler(0x21,&handle_keyboard_interrupt, NULL); + +// devfs_publish_device(DEVICE_NAME, NULL, &keyboard_hooks); + + return 0; +} + +const char **publish_devices(void) +{ + static const char *devices[] = { + DEVICE_NAME, + NULL + }; + + return devices; +} + +device_hooks *find_device(const char *name) +{ + if (!strcmp(name, DEVICE_NAME)) + return &keyboard_hooks; + + return NULL; +} + +status_t init_driver() +{ + return 0; +} + +void uninit_driver() +{ +} diff --git a/src/add-ons/kernel/drivers/arch/x86/ps2mouse/Jamfile b/src/add-ons/kernel/drivers/arch/x86/ps2mouse/Jamfile new file mode 100644 index 0000000000..8e6978cc10 --- /dev/null +++ b/src/add-ons/kernel/drivers/arch/x86/ps2mouse/Jamfile @@ -0,0 +1,3 @@ +SubDir OBOS_TOP src add-ons kernel drivers arch x86 ps2mouse ; + +KernelObjects ps2mouse.c : -fno-pic -D_KERNEL_MODE -D_PS2MOUSE_ ; diff --git a/src/add-ons/kernel/drivers/arch/x86/ps2mouse/ps2mouse.c b/src/add-ons/kernel/drivers/arch/x86/ps2mouse/ps2mouse.c new file mode 100644 index 0000000000..a3394b8bae --- /dev/null +++ b/src/add-ons/kernel/drivers/arch/x86/ps2mouse/ps2mouse.c @@ -0,0 +1,362 @@ +/* + * ps2mouse.c: + * PS/2 mouse device driver for NewOS and OpenBeOS. + * Author: Elad Lahav (elad@eldarshany.com) + * Created: 21.12.2001 + * Modified: 11.1.2002 + */ + +/* + * A PS/2 mouse is connected to the IBM 8042 controller, and gets its + * name from the IBM PS/2 personal computer, which was the first to + * use this device. All resources are shared between the keyboard, and + * the mouse, referred to as the "Auxiliary Device". + * I/O: + * ~~~ + * The controller has 3 I/O registers: + * 1. Status (input), mapped to port 64h + * 2. Control (output), mapped to port 64h + * 3. Data (input/output), mapped to port 60h + * Data: + * ~~~~ + * Since a mouse is an input only device, data can only be read, and + * not written. A packet read from the mouse data port is composed of + * three bytes: + * byte 0: status byte, where + * - bit 0: Y overflow (1 = true) + * - bit 1: X overflow (1 = true) + * - bit 2: MSB of Y offset + * - bit 3: MSB of X offset + * - bit 4: Syncronization bit (always 1) + * - bit 5: Middle button (1 = down) + * - bit 6: Right button (1 = down) + * - bit 7: Left button (1 = down) + * byte 1: X position change, since last probed (-127 to +127) + * byte 2: Y position change, since last probed (-127 to +127) + * Interrupts: + * ~~~~~~~~~~ + * The PS/2 mouse device is connected to interrupt 12, which means that + * it uses the second interrupt controller (handles INT8 to INT15). In + * order for this interrupt to be enabled, both the 5th interrupt of + * the second controller AND the 3rd interrupt of the first controller + * (cascade mode) should be unmasked. + * The controller uses 3 consecutive interrupts to inform the computer + * that it has new data. On the first the data register holds the status + * byte, on the second the X offset, and on the 3rd the Y offset. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define _PS2MOUSE_ +#include + +#define DEVICE_NAME "ps2mouse" + +///////////////////////////////////////////////////////////////////////// +// interrupt + +/* + * handle_mouse_interrupt: + * Interrupt handler for the mouse device. Called whenever the I/O + * controller generates an interrupt for the PS/2 mouse. Reads mouse + * information from the data port, and stores it, so it can be accessed + * by read() operations. The full data is obtained using 3 consecutive + * calls to the handler, each holds a different byte on the data port. + * Parameters: + * void*, ignored + * Return value: + * int, ??? + */ +static int handle_mouse_interrupt(void* data) +{ + char c; + static int next_input = 0; + + // read port + c = in8(PS2_PORT_DATA); + + // put port contents in the appropriate data member, according to + // current cycle + switch(next_input) { + // status byte + case 0: + md_int.status = c; + break; + + // x-axis change + case 1: + md_int.delta_x += c; + break; + + // y-axis change + case 2: + md_int.delta_y += c; + + // check if someone is waiting to read data + if(in_read) { + // copy data to read structure, and release waiting process + memcpy(&md_read, &md_int, sizeof(mouse_data)); + memset(&md_int, 0, sizeof(mouse_data)); + in_read = false; + release_sem_etc(mouse_sem, 1, B_DO_NOT_RESCHEDULE); + } // if + break; + } // switch + + next_input = (next_input + 1) % 3; + return INT_NO_RESCHEDULE; +} // handle_mouse_interrupt + +///////////////////////////////////////////////////////////////////////// +// file operations + +/* + * mouse_open: + */ +static int mouse_open(const char *name, uint32 flags, void **cookie) +{ + *cookie = NULL; + return 0; +} // mouse_open + +/* + * mouse_close: + */ +static int mouse_close(void * cookie) +{ + return 0; +} // mouse_close + +/* + * mouse_freecookie: + */ +static int mouse_freecookie(void * cookie) +{ + return 0; +} // mouse_freecookie + +/* + * mouse_read: + * Gets a mouse data packet. + * Parameters: + * void *, ignored + * void*, pointer to a buffer that accepts the data + * off_t, ignored + * ssize_t, buffer size, must be at least the size of the data packet + */ +static ssize_t mouse_read(void * cookie, off_t pos, void* buf, size_t *len) +{ + // inform interrupt handler that data is being waited for + in_read = true; + + // wait until there is data to read + if(acquire_sem_etc(mouse_sem, 1, B_CAN_INTERRUPT, 0) == + EINTR) { + return 0; + } // if + + // verify user's buffer is of the right size + if(*len < PACKET_SIZE) { + *len = 0; + /* XXX - should return an error here */ + return 0; + } + + // copy data to user's buffer + ((char*)buf)[0] = md_read.status; + ((char*)buf)[1] = md_read.delta_x; + ((char*)buf)[2] = md_read.delta_y; + + *len = PACKET_SIZE; + return 0; +} // mouse_read + +/* + * mouse_write: + */ +static ssize_t mouse_write(void * cookie, off_t pos, const void *buf, size_t *len) +{ + *len = 0; + return EROFS; +} // mouse_write + +/* + * mouse_ioctl: + */ +static int mouse_ioctl(void * cookie, uint32 op, void *buf, size_t len) +{ + return EINVAL; +} // mouse_ioctl + +/* + * function structure used for file-op registration + */ +device_hooks ps2_mouse_hooks = { + &mouse_open, + &mouse_close, + &mouse_freecookie, + &mouse_ioctl, + &mouse_read, + &mouse_write, + NULL, + NULL, +// NULL, +// NULL +}; // ps2_mouse_hooks + +///////////////////////////////////////////////////////////////////////// +// initialization + +/* + * wait_write_ctrl: + * Wait until the control port is ready to be written. This requires that + * the "Input buffer full" and "Output buffer full" bits will both be set + * to 0. + */ +static void wait_write_ctrl() +{ + while(in8(PS2_PORT_CTRL) & 0x3); +} // wait_for_ctrl_output + +/* + * wait_write_data: + * Wait until the data port is ready to be written. This requires that + * the "Input buffer full" bit will be set to 0. + */ +static void wait_write_data() +{ + while(in8(PS2_PORT_CTRL) & 0x2); +} // wait_write_data + +/* + * wait_read_data: + * Wait until the data port can be read from. This requires that the + * "Output buffer full" bit will be set to 1. + */ +static void wait_read_data() +{ + while((in8(PS2_PORT_CTRL) & 0x1) == 0); +} // wait_read_data + +/* + * write_command_byte: + * Writes a command byte to the data port of the PS/2 controller. + * Parameters: + * unsigned char, byte to write + */ +static void write_command_byte(unsigned char b) +{ + wait_write_ctrl(); + out8(PS2_CTRL_WRITE_CMD, PS2_PORT_CTRL); + wait_write_data(); + out8(b, PS2_PORT_DATA); +} // write_command_byte + +/* + * write_aux_byte: + * Writes a byte to the mouse device. Uses the control port to indicate + * that the byte is sent to the auxiliary device (mouse), instead of the + * keyboard. + * Parameters: + * unsigned char, byte to write + */ +static void write_aux_byte(unsigned char b) +{ + wait_write_ctrl(); + out8(PS2_CTRL_WRITE_AUX, PS2_PORT_CTRL); + wait_write_data(); + out8(b, PS2_PORT_DATA); +} // write_aux_byte + +/* + * read_data_byte: + * Reads a single byte from the data port. + * Return value: + * unsigned char, byte read + */ +static unsigned char read_data_byte() +{ + wait_read_data(); + return in8(PS2_PORT_DATA); +} // read_data_byte + +/* + * mouse_dev_init: + * Called by the kernel to setup the device. Initializes the driver. + * Parameters: + * kernel_args*, ignored + * Return value: + * int, 0 if successful, negative error value otherwise + */ +status_t init_hardware() +{ + dprintf("Initializing PS/2 mouse\n"); + + // init device driver + memset(&md_int, 0, sizeof(mouse_data)); + + // register interrupt handler + int_set_io_interrupt_handler(INT_BASE + INT_PS2_MOUSE, + &handle_mouse_interrupt, NULL); + + // must enable the cascade interrupt + arch_int_enable_io_interrupt(INT_BASE + INT_CASCADE); + + // enable auxilary device, IRQs and PS/2 mouse + write_command_byte(PS2_CMD_DEV_INIT); + write_aux_byte(PS2_CMD_ENABLE_MOUSE); + + // controller should send ACK if mouse was detected + if(read_data_byte() != PS2_RES_ACK) { + dprintf("No PS/2 mouse found\n"); + return -1; + } // if + + dprintf("A PS/2 mouse has been successfully detected\n"); + + // create the mouse semaphore, used for synchronization between + // the interrupt handler and the read() operation + mouse_sem = create_sem(0, "ps2_mouse_sem"); + if(mouse_sem < 0) + panic("failed to create PS/2 mouse semaphore!\n"); + + // register device file-system like operations +// devfs_publish_device("ps2mouse", NULL, &ps2_mouse_hooks); + + return 0; +} // mouse_dev_init + + +const char **publish_devices(void) +{ + static const char *devices[] = { + DEVICE_NAME, + NULL + }; + + return devices; +} + +device_hooks *find_device(const char *name) +{ + if (!strcmp(name, DEVICE_NAME)) + return &ps2_mouse_hooks; + + return NULL; +} + +status_t init_driver() +{ + return 0; +} + +void uninit_driver() +{ +}