/*****************************************************************************
serial mouse driver
*****************************************************************************/
#include <kernel/kernel.h>
#include <kernel/driver.h>
#include <kernel/sys.h>
#include <os/mouse.h>
#include <errno.h>
#define DEBUG
#include <kernel/debug.h>
/*!
* \ingroup drivers
* \defgroup sermouse Serial mouse
* @{
*/
//#define peek40(OFF) *((word*) (0x400 + (OFF))) // xxx - FIX!
#define BUF_SIZE 64
typedef struct /* circular queue */
{
unsigned char *data;
unsigned size, in_ptr, out_ptr;
} queue_t;
typedef struct
{
device_t dev;
unsigned char _irq, _buffer[BUF_SIZE];
unsigned short _io_adr;
queue_t _queue;
/* which COM port to use (1=COM1, 2=COM2, etc.) */
unsigned char _com;
} SerMouse;
/*****************************************************************************
*****************************************************************************/
void enable_irq_at_8259(unsigned short irq)
{
unsigned char mask;
if(irq < 8)
{
mask = 1 << irq;
out(0x21, in(0x21) & ~mask);
}
else if(irq < 16)
{
irq -= 8;
mask = 1 << irq;
out(0xA1, in(0xA1) & ~mask);
out(0x21, in(0x21) & ~0x04);
}
}
/*****************************************************************************
*****************************************************************************/
void disable_irq_at_8259(unsigned short irq)
{
unsigned char mask;
if(irq < 8)
{
mask = 1 << irq;
out(0x21, in(0x21) | mask);
}
else if(irq < 16)
{
irq -= 8;
mask = 1 << irq;
out(0xA1, in(0xA1) | mask);
}
}
static __inline__ unsigned crit_begin(void)
{
unsigned ret_val;
__asm__ __volatile__("pushfl\n"
"popl %0\n"
"cli"
: "=a"(ret_val)
:);
return ret_val;
}
static __inline__ void crit_end(unsigned flags)
{
__asm__ __volatile__("pushl %0\n"
"popfl"
:
: "m"(flags)
);
}
int inq(queue_t *q, unsigned char data)
{
unsigned flags, temp;
flags = crit_begin();
temp = q->in_ptr + 1;
if(temp >= q->size)
temp = 0;
/* if in_ptr reaches out_ptr, the queue is full */
if(temp == q->out_ptr)
{
crit_end(flags);
return -1;
}
q->data[q->in_ptr] = data;
q->in_ptr = temp;
crit_end(flags);
return 0;
}
/*****************************************************************************
*****************************************************************************/
int deq(queue_t *q, unsigned char *data)
{
unsigned flags;
flags = crit_begin();
/* if in_ptr == out_ptr, the queue is empty */
if(q->in_ptr == q->out_ptr)
{
crit_end(flags);
return -1;
}
*data = q->data[q->out_ptr];
q->out_ptr++;
if(q->out_ptr >= q->size)
q->out_ptr = 0;
crit_end(flags);
return 0;
}
bool mseRequest(device_t* dev, request_t* req)
{
unsigned char temp;
SerMouse *ctx = (SerMouse*) dev;
TRACE2("{%c%c}", req->code / 256, req->code % 256);
switch (req->code)
{
case DEV_READ:
if (req->params.read.length < sizeof(mouse_packet_t))
{
TRACE1("%d: too small\n", req->params.read.length);
req->result = EBUFFER;
return false;
}
devStartRequest(dev, req);
return true;
case DEV_OPEN:
case DEV_CLOSE:
hndSignal(req->event, true);
return true;
case DEV_REMOVE:
/* disable interrupts at serial chip */
out(ctx->_io_adr + 1, 0);
/* disable interrupts at 8259s */
disable_irq_at_8259(ctx->_irq);
hndFree(ctx);
hndSignal(req->event, true);
return true;
case DEV_ISR:
temp = in(ctx->_io_adr + 5);
if (temp & 0x01)
{
temp = in(ctx->_io_adr + 0);
inq(&ctx->_queue, temp);
}
in(ctx->_io_adr + 2);
// req->event ignored here
return true;
}
req->result = ENOTIMPL;
return false;
}
/*****************************************************************************
*****************************************************************************/
#define MAX_ID 16
bool STDCALL INIT_CODE drvInit(driver_t* drv)
{
static const unsigned char com_to_irq[] =
{
4, 3, 4, 3
};
static const word io_port[] =
{
0x3f8, 0x2f8, 0x3f8, 0x2f8
};
/**/
unsigned char b, id[MAX_ID], *ptr;
unsigned temp;
SerMouse *ctx;
ctx = hndAlloc(sizeof(SerMouse), NULL);
ctx->dev.driver = drv;
ctx->dev.request = mseRequest;
ctx->dev.req_first = ctx->dev.req_last = NULL;
ctx->_queue.data = ctx->_buffer;
ctx->_queue.size = BUF_SIZE;
ctx->_queue.in_ptr = ctx->_queue.out_ptr = 0;
ctx->_com = 1;
if (ctx->_com < 1 || ctx->_com > 4)
{
TRACE1("Invalid COM port %u (must be 1-4)\n", ctx->_com);
return false;
}
/* get COM port I/O address from BIOS data segment */
//ctx->_io_adr = peek40(0 + (ctx->_com - 1) * 2);
ctx->_io_adr = io_port[ctx->_com - 1];
if(ctx->_io_adr == 0)
{
TRACE1("Sorry, no COM%u serial port on this PC\n", ctx->_com);
return false;
}
TRACE1("activating mouse on COM%u...\n", ctx->_com);
ctx->_irq = com_to_irq[ctx->_com - 1];
/* install handler */
devRegisterIrq((device_t*) ctx, ctx->_irq, true);
/* set up serial chip
turn off handshaking lines to power-down mouse, then delay */
out(ctx->_io_adr + 4, 0);
msleep(500);
out(ctx->_io_adr + 3, 0x80); /* set DLAB to access baud divisor */
out(ctx->_io_adr + 1, 0); /* 1200 baud */
out(ctx->_io_adr + 0, 96);
out(ctx->_io_adr + 3, 0x02); /* clear DLAB bit, set 7N1 format */
out(ctx->_io_adr + 2, 0); /* turn off FIFO, if any */
/* activate Out2, RTS, and DTR to power the mouse */
out(ctx->_io_adr + 4, 0x0B);
/* enable receiver interrupts */
out(ctx->_io_adr + 1, 0x01);
/* enable interrupts at 8259s */
enable_irq_at_8259(ctx->_irq);
/* wait a moment to get ID bytes from mouse. Microsoft mice just return
a single 'M' byte, some 3-button mice return "M3", my Logitech mouse
returns a string of bytes. I could find no documentation on these,
but I figured out some of it on my own. */
ptr = id;
for(temp = 750; temp != 0; temp--)
{
while(deq(&ctx->_queue, &b) == 0)
{
TRACE1("%02X ", b);
*ptr = b;
ptr++;
if(ptr >= id + MAX_ID)
goto FOO;
}
msleep(1);
}
TRACE0("\n");
if (id == ptr)
TRACE0("No serial mouse found\n");
FOO:
if(ptr >= id + 11)
{
/* find the 'M' */
for(ptr = id; ptr < id + MAX_ID - 7; ptr++)
{
if(*ptr == 'M')
break;
}
if(ptr < id + MAX_ID)
{
/* four bytes that each encode 4 bits of the numeric portion
of the PnP ID. Each byte has b4 set (i.e. ORed with 0x10) */
temp = (ptr[8] & 0x0F);
temp <<= 4;
temp |= (ptr[9] & 0x0F);
temp <<= 4;
temp |= (ptr[10] & 0x0F);
temp <<= 4;
temp |= (ptr[11] & 0x0F);
/* three bytes that each encode one character of the character portion
of the PnP ID. Each byte has b5 set (i.e. ORed with 0x20) */
TRACE4("mouse PnP ID: %c%c%c%04X\n",
'@' + (ptr[5] & 0x1F),
'@' + (ptr[6] & 0x1F),
'@' + (ptr[7] & 0x1F), temp);
}
/* example: Logitech serial mouse LGI8001
('L' - '@') | 0x20 == 0x2C
('G' - '@') | 0x20 == 0x27
('I' - '@') | 0x20 == 0x29
('8' - '0') | 0x10 == 0x18
('0' - '0') | 0x10 == 0x10
('0' - '0') | 0x10 == 0x10
('1' - '0') | 0x10 == 0x11 */
}
return true;
}
//@}