#include <linux/init.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <asm/system.h>
#include <asm/irq.h>
#include <linux/delay.h>
#include <linux/string.h>
#include "serial.h"
MODULE_AUTHOR("Felixls");
MODULE_LICENSE("Dual BSD/GPL");
int ser_busy = SER_FREE;
static int ser_open(struct inode * inode, struct file * file)
{
int codigo;
unsigned int minor = MINOR(inode->i_rdev);
printk(KERN_ALERT " SER: opening...\n");
if (minor != 0)
return -ENODEV;
if (ser_busy==SER_BUSY)
return -EBUSY;
printk (KERN_ALERT "SER: serial abierto.\n");
outb(0x80, SER_BASE + 3); // Set DLAB ON
outb(0x0C, SER_BASE + 0); // Set Baud rate DL low (9600) (DLL) 0x0C
/* 0x01 = 115.200 BPS */
/* 0x02 = 57.600 BPS */
/* 0x03 = 38.400 BPS */
/* 0x06 = 19.200 BPS */
/* 0x0C = 9.600 BPS */
/* 0x18 = 2.400 BPS */
outb(0x00, SER_BASE + 1); // Set Baud rate DL high (DLM)
outb(0x03, SER_BASE + 3); // 8bits, No Parity, 1 Stop bit (LCR)
outb(0xC7, SER_BASE + 2); // Enable FIFO Control Register (FCR)
outb(0x0B, SER_BASE + 4); // Turn on DTR, RTS (MCR)
ser_busy = SER_BUSY;
return 0;
}
int serial_recieved()
{
return inb(SER_IN + 5) & 1;
}
int is_transmit_empty()
{
return inb(SER_IN + 5) & 0x20;
}
static ssize_t ser_read (struct file *file, char *buf, size_t count, loff_t *nose)
{
int i, c;
int readed = 0;
int timeout = 100;
char *readbuffer;
readbuffer = kmalloc(count, GFP_KERNEL);
for(i = 0; i < count; i++)
{
c = 0;
while (serial_recieved() == 0 && c < timeout)
{
msleep(1);
c++;
continue;
}
if (c >= timeout) break;
readbuffer[i] = inb(SER_IN);
readed++;
}
if (readed > 0)
__copy_to_user(buf, readbuffer, readed);
kfree(readbuffer);
if (readed == 0 && c >= timeout)
return -EINTR;
return readed;
}
static ssize_t ser_write(struct file *file, const char *buf,size_t count, loff_t *nose)
{
char *writebuffer;
int i;
writebuffer = kmalloc(count, GFP_KERNEL);
__copy_from_user(writebuffer, buf, count);
int written = 0;
for(i=0; i<count; i++)
{
while (is_transmit_empty()==0)
{
msleep(1);
continue;
}
outb(writebuffer[i], SER_OUT);
written++;
}
kfree(writebuffer);
return written;
}
static int ser_release (struct inode *inode, struct file *file)
{
ser_busy = SER_FREE;
printk (KERN_ALERT "SER: serial cerrado.\n");
return 0;
}
struct file_operations ser_fops=
{
.owner = THIS_MODULE,
.read = ser_read,
.write = ser_write,
.open = ser_open,
.release = ser_release,
};
static int serial_init(void)
{
if (register_chrdev(SER_MAYOR, "ser", &ser_fops))
{
printk(KERN_ALERT "ERROR: init_module ha fallado instalando SER driver...\n");
return -EIO;
}
printk(KERN_ALERT "modulo instalado!\n");
return 0;
}
static void serial_exit(void)
{
if (ser_busy)
{
printk(KERN_ALERT "WARNG: SER driver ocupado, no se pudo remover....\n");
return;
}
if (unregister_chrdev(SER_MAYOR,"ser") != 0)
printk(KERN_ALERT "ERROR: cleanup_module ha fallado, no se puede desregistrar SER deiver...\n");
else
printk(KERN_ALERT "modulo desinstalado!\n");
}
module_init(serial_init);
module_exit(serial_exit);