LQ Newbie
Registered: May 2006
Posts: 2
Rep:
|
device driver for UART (write illegal seek - error)
2. uart1.c
I am able to load the module into the kernel and device is getting registered by allocating the major no for the device.
Then, i have created the device file using mknod(Resultant major no). I have wriiten an application program which uses this
device driver functions like open, write, ioctl. When i run the application program, it is saying that
-----------------------------------------------
Open call success : success
Write call: Illegal seek
Before 4800
After 1001
Close call : Illegal seek
------------------------------------------------
1.Open call success is successful
2.Even ioctl is working, bcoz i am using 2 functions one is to set the baud rate(default 4800) and then setting the baud rate
to 1000 and displaying it.
3. But write and close is not working. I think control is not going inside the write function in the module itself.
Bcoz for debugging i gave few printk statements at the start and at the middle, end. But i am not able to get that msg.
the code (uart.h)
//To make the kernel header contetnts available in the kernel headersto users
#define __KERNEL__
//To insert our module into the kernel
#include <linux/module.h> // To load ur module into kernel
#include <linux/sched.h> // To access the current process
#include <asm/current.h> // current pointer to the "struct task struct"
#include <linux/ioport.h> // To detect,reserve and allocate system resources(IO ports)
#include <linux/init.h> // To mark driver entry and exit point to the kernel
#include <linux/wait.h> // To create a waitng queue of sleeping processes
#include <linux/fs.h> // For file operations
#include <linux/mm.h> // For GFP_KERNEL flag for allocating a page
#include <asm/errno.h> // To report error numbers
#include <asm/uaccess.h> // To allow cross-space access for kernel
#include <sys/io.h> // To define x80 io operations
#define baseaddr 0x3f8 // Io address of UART device
#define IER baseaddr+1 // Interrupt enable Reg
#define IIR baseaddr+2 // Interrupt Identification Reg
#define FCR baseaddr+2
#define LCR baseaddr+3 // Line Control Reg
#define MCR baseaddr+4 // Modem Control Reg
#define LSR baseaddr+5 // Line status Reg
#define MSR baseaddr+6 // Modem status Reg
//header.........
#define MAXBUFSIZE 2048 // Max buffer size
#define DEFAULTFIFO 0x00
#define DEFAULTMCR 0x0c
#define DEFAULTBAUD 4800
#define CLEAR 0x00
#define UART_IRQ 4
#define DLABSET 0x80
#define DEFAULTDLL 0x18
#define DEFAULTDLM 0x00
#define LOOPBACK 0x10
#define DEFAULTFF 0x03
#define MAX_PAGE 2048
#define RLS 0x06 // Reciever Line status
#define RDA 0x04 // Reciever data available
#define THRE 0x02 // Transmitter holding Reg empty
/*#define MSR 0x00*/
#define IOCTL_MAGIC 'x' // To select the frame format of 8 bits
#define BAUD_MAX 115200
#define REF_FREEQUENCY 1843200
// Giving unique number to ioctl commands
#define UART_SETBAUD _IOW(IOCTL_MAGIC,1,sizeof(int))
#define UART_GETBAUD _IOR(IOCTL_MAGIC,2,sizeof(int))
#define UART_SETFRAME _IOW(IOCTL_MAGIC,3,sizeof(int))
#define UART_SETFIFO _IOR(IOCTL_MAGIC,4,sizeof(int))
wait_queue_head_t wQ;
wait_queue_head_t rQ;
unsigned long write_head=0;
unsigned long write_tail=0;
unsigned long write_buffer=0;
unsigned long read_buffer=0;
unsigned long read_head=0;
unsigned long read_tail=0;
int open_cnt;
// Function declarations
void UART_Transmit(void);
void UART_Recieve(void);
void UART_Initialize(int);
int UART_Init();
int UART_Check();
int UART_Loopback();
void UART_Cleanup();
void UART_Clear();
int UART_Open(struct inode *uart_inode,struct file * uart_file);
int UART_Close(struct inode *in,struct file *fi);
ssize_t UART_Write(struct file *,const char *,size_t,loff_t *);
ssize_t UART_Read(struct file *fi,const char *buf,size_t ncount,loff_t *pos);
int read_copy(unsigned long,char *);
int write_copy(unsigned long,const char *);
int UART_Ioctl(struct inode *in,struct file *fi,unsigned int cmd,unsigned long arg);
(uart.c)
#include "uart.h" // Header file for the program
MODULE_LICENSE("Dual BSD/GPL");
int major_num=0;
int fifo=1;
struct file_operations UART_fops={ /* UART file operations */
read : UART_Read,
write : UART_Write,
open : UART_Open,
release : UART_Close,
ioctl : UART_Ioctl,
};
int UART_Init()
{
UART_Check(); // To check and allocate io port
return 0;
}
void UART_Cleanup()
{
UART_Clear();
release_region(baseaddr,8);
unregister_chrdev(major_num,"UART");
}
int UART_Check()
{
int check_val,loop_val;
if((check_val=check_region(baseaddr,8))==0) //For UART port address
request_region(baseaddr,8,"UART");
else
{
printk("Busy\n");
release_region(baseaddr,8);
request_region(baseaddr,8,"UART");
}
printk("Region For device allocated\n");
loop_val=UART_Loopback(); // call to check UART loopback working
printk("Loopback value=%d\n",loop_val);
if(loop_val!=0)
release_region(baseaddr,8);
else
major_num=register_chrdev(0,"UART",&UART_fops);
printk("Major No=%d\n",major_num);
return 0;
}
int UART_Loopback()
{
int ret_val,sent_val=100;
int i;
outb(LOOPBACK,MCR); // set bit 4 of MCR for Loopback mode(0x10)
outb(DLABSET,LCR); // set bit 7 of LCR to access devisor latches
outb(DEFAULTDLL,baseaddr); // load lower devisor latch with 24(dec) or 0x18
outb(DEFAULTFF,LCR); // Load default frame format 8-bit transfer
outb(DEFAULTDLM,IER); // Load higher devisor latch with 00(dec) or 0x00
outb(sent_val,baseaddr); // send 100 to 0x3f8 port address
for(i=0;i<100000000;i++);
ret_val=inb(baseaddr); // read back value from the port
if(ret_val==sent_val)
return 0;
else
return -1;
}
void UART_Clear() // Function to clear UART registers
{
int clear_val,i;
outb(CLEAR,MCR);
outb(CLEAR,LCR);
outb(CLEAR,LSR);
outb(CLEAR,IER);
outb(CLEAR,IIR);
for(i=0;i<100000000;i++);
clear_val=inb(IER);
printk("After clearing value IER=%x\n IIR=%x\n",clear_val,inb(IIR));
}
void UART_RLS()
{
char lsr;printk("inside RLS handler\n");
lsr=inb(LSR); // Reading the reciever line status register contents
if(lsr & 0x02)
{
printk("Overrun error\n"); // checking the overflow error bit
inb(baseaddr);
}
else if(lsr & 0x08) // checking for the frame error bit
printk("FRAME ERROR\n");
else if( lsr & 0x04) // checking for the parity error bit\n");
printk("Parity error\n");
else if(lsr & 0x10) // checking for the break interrupt bit
printk("Break interrupt\n");
else if(lsr & 0x01)
printk("Data avaialable\n"); // check complete character has been recieved
}
void UART_Recieve()
{
if( ( ( read_head - read_tail) == MAXBUFSIZE) || ( (read_tail-read_head)==1) )
// If the buffer is full wait to read the data from the kernel buffer
{
*( (char *)read_head)=inb(baseaddr);
wake_up_interruptible(&rQ);
return;
}
// If the read head is pointing to end make it point begining of the buffer
if ( read_head== (read_buffer+MAXBUFSIZE) )
{
*( (char *)read_head)=inb(baseaddr);
read_head=read_buffer;
}
// read the data from the reciever buffer
else
{
*( (char *)read_head)=inb(baseaddr);
printk("In the interrupt : %c\n",*((int *)read_head));
read_head++;
}
// Wake up the processes waiting for the release of the interrupt
inb(MSR);
wake_up_interruptible(&rQ);
// End of the reciever interrupt routine
}
void UART_Transmit()
{
int ier_val;
// After all the bytes transferred ,disable
printk("\n In transmit\n");
if(write_head == write_tail)
{
ier_val=inb(IER);
ier_val=( ier_val & 0x0d);
inb(MSR);
outb(ier_val,IER);
wake_up_interruptible(&wQ);
// wake up the processes waiting
return;
}
// If the tail is pointing to the end of the buffer make
if(write_tail==write_buffer+MAXBUFSIZE)
{
// out of the byte to the port
outb(*(char *)write_tail,baseaddr);
write_tail=write_buffer;
}
// write the bytes into the transmitting holding register
else
{
outb(*((char *)write_tail),baseaddr);
write_tail++;
}
// wake up the processes waiting for thr release of the interrupt
inb(MSR);
wake_up_interruptible(&wQ);
}
void UART_IHandlers(int irq,void * device_Id,struct pt_regs *regs)
{
int iir_val;
iir_val=inb(IIR);
iir_val=(iir_val & 0x0f);
printk("inside handlers\n");
printk("iir value : %d\n",iir_val);
switch(iir_val)
{
case RLS :
printk("Inside RLS case\n");
UART_RLS();
break;
case RDA :
printk("In the RDA interrupt\n");
UART_Recieve();
break;
case THRE :
// Transmitter holding register is empty
printk("In the THRE interrupt\n");
UART_Transmit();
break;
case MSR :
// for the modem status change
break;
}
}
//Function to allocate IRQ's, set baud rate and set regs to initial values
void UART_Initialise()
{
int check_irq,i;
outb(0x00,IER);
check_irq=request_irq(UART_IRQ,UART_IHandlers,SA_INTERRUPT,"UART",0);
if(check_irq<0)
{
free_irq(UART_IRQ,0);
check_irq=request_irq(UART_IRQ,UART_IHandlers,SA_INTERRUPT,"UART",0);
}
outb(0x0d,IER); // enable all interrupts except THRE
outb(DLABSET,LCR); // set bit 7 of LCR to access devisor latches
outb(DEFAULTDLL,baseaddr); // load lower devisor latch with 24(dec) or 0x28
outb(DEFAULTDLM,IER); // load higher devisor latch with 0x00 or 00(dec)
outb(DEFAULTFF,LCR); // load default frame format 8-bit transfer
outb(DEFAULTMCR,MCR); // load MCR with 0x0c to disable OUT1 and OUT2 pins
// Enabling Modem
outb(DEFAULTFIFO,FCR); // Disable FIFO mode operation of UART
outb(0x00,IER); // Disable all interrupts in IER reg
i=0;
while(i<1000000)
{
i++;
}
outb(0x0d,IER); // Enable interrupts except THRE
i=0;
while(i<1000000)
{
i++;
}
}
int UART_Open(struct inode *uart_inode,struct file *uart_file)
{
MOD_INC_USE_COUNT;
printk("\nOpening the device\n");
init_waitqueue_head(&rQ); // Initilize the read queue
init_waitqueue_head(&wQ); // Initialize the write queue
write_buffer = get_free_page(GFP_KERNEL); // Returns sysetm dependent
// Page size with starting addr
printk("Write buffer=%lu\n",write_buffer);
write_tail=write_buffer; // Assign write_tail and write_head with start address
write_head=write_buffer;
read_buffer=write_buffer + MAXBUFSIZE; // assign buffer for read operation
read_tail=read_buffer; // Devide page allocated b/w write and read buffers
read_head=read_buffer;
UART_Clear(); // Function to clear all UART Registers
UART_Initialise(); // Initialize regs and interrupts
return 0;
}
int write_copy(unsigned long ncount,const char *buf)
{
int i=0; // For the delay
int ier_val;
unsigned long ntemp; // for splitting the copying process
// If the write header is within the end of the buffer
if( (write_head + ncount) < (write_buffer + MAXBUFSIZE) )
{
copy_from_user((unsigned char*)write_head,buf,ncount);
printk("<1> \n Copying to Kernel space\n");
write_head+=ncount;
}
/* If the write head reaches the end of the buffer. Copy upto the end. Make write head to the point to the beginig
for the remaining bytes to copy*/
else
{
ntemp=(write_buffer+MAXBUFSIZE)-write_head;
ncount-=ntemp;
copy_from_user((unsigned char*)write_head,buf,ntemp);
buf+=ntemp;
write_head=write_buffer;
copy_from_user((unsigned char*)write_head,buf,ncount);
write_head+=ncount;
}
// Set the transmitter enable bit in Interrupt Enable Register
ier_val=(ier_val|0x02);
outb(ier_val,IER);
// Delay to set the register values
while(i<1000)
i++;
return 0;
}
ssize_t UART_Write(struct file *fi,const char *buf,size_t ncount,loff_t *pos)
{
int nAvailable=0; // For the availability of space in
int origin_count=0,i; // To return no of bytes transmitted
printk("In write\n");
for(i=0;i<100000000;i++);
// Find availability bytes to write
begin:
if(write_head==write_tail)
nAvailable=MAXBUFSIZE-1;
else
nAvailable=((MAXBUFSIZE - (write_head-write_tail)) % MAXBUFSIZE)-1;
// For the non blocking operation
if( (fi->f_flags) & (O_NONBLOCK))
{
printk("\nNONBLOCKING\n");
if(ncount>nAvailable)
ncount=nAvailable;
write_copy(ncount,buf); // Copy the bytes from the user buffer
i=0;
while(i<100000000)
i++;
origin_count=ncount; // Number of bytes transmitted
}
// For the BLOCKING operation
else
{
// If the bytes to be transmitted are less than the available free space
printk("\nBLOCKING\n");
if(ncount<=nAvailable)
{
write_copy(ncount,buf);
i=0;
while(i<100000000)
i++;
origin_count+=ncount;
}
// If the bytes to be transmitted are more than the available free space
else
{
ncount-=nAvailable;
origin_count+=nAvailable;
// Copy the available bytes and transfer
write_copy(nAvailable,buf);
i=0;
while(i<10000000)
i++;
buf+=nAvailable;
// Sleep till the kernel buffer frees some space after the transmission
interruptible_sleep_on(&wQ);
goto begin; // To transmit the remainig bytes
}
}
return origin_count; // Return the transmitted count
}
ssize_t UART_Read(struct file *fi,const char *buf,size_t ncount,loff_t *pos)
{
unsigned long norg_count=0; // To return the no of bytes read
unsigned long navailable; // To return no of bytes available to read
// Verify the user space memory for access to kernel
if(!access_ok(VERIFY_WRITE,(void *)buf,ncount))
{
printk("ERROR IN ACCESS\n");
return -1;
}
printk("I AM IN READ PERATION\n");
// Find the available bytes to read
begin:
if(read_head>=read_tail)
navailable=(read_head-read_tail);
else
navailable=MAX_PAGE-(read_tail-read_head);
// Checks wheather in blocking mode or non-blocking mode
if( (fi->f_flags) & (O_NONBLOCK) )
{
// Non-blocking condition
if(ncount > navailable)
ncount=navailable;
read_copy(ncount,buf);
norg_count=ncount;
}
// If it is blocking and the no of bytes to read is greater than the available,then sleep is called
else
{
// If nothing is there to read process is made to sleep
if(navailable==0)
{
interruptible_sleep_on(&rQ);
goto begin;
}
// Blocking with count > available
if(ncount>navailable)
{
ncount-=navailable;
read_copy(navailable,buf); // Read available bytes
norg_count+=navailable;
buf+=navailable;
interruptible_sleep_on(&rQ);
goto begin;
}
// Read the bytes if count < =available
else
{
read_copy(ncount,buf);
norg_count+=ncount;
}
}
return norg_count; // Return the no of bytes read
}
int read_copy(unsigned long x,char *buf)
{
int temp; // For copyinf the part of the data to be read
// If the data count to be read is within the end of the read buffer
if( (read_tail+x) <= (read_buffer+MAXBUFSIZE) )
{
copy_to_user(buf,(void *)read_tail,x);
read_tail+=x;
}
// Copy the data 2 times
else
{
temp=read_buffer+MAXBUFSIZE-read_tail;
x-=temp;
copy_to_user(buf,(unsigned char*)read_tail,(unsigned long )temp);
read_tail=read_buffer;
buf+=temp;
copy_to_user(buf,(unsigned char*)read_tail,(unsigned long) x);
read_tail+=x;
}
return 0;
}
int UART_Ioctl(struct inode *in, struct file *fi, unsigned int cmd, unsigned long arg)
{
int baud_rate; // To hold the baud rate
int baud_value; // To hold the baud value
int count=10000000; // Loop variable used to set the delay
int ls, ms, length;
int cur, new;
int trig_level;
int lcr_val;
switch(cmd)
{
/*----------------------------------------------------------------------------------------------------------------*/
case UART_SETBAUD:
baud_rate=arg;
lcr_val=inb(LCR);
outb(DLABSET,LCR); // Enable the DLAB register
// Check permission
if(!capable(CAP_SYS_ADMIN))
return -EPERM;
// Check validity of baud rate
if( (baud_rate<=0) || (baud_rate>BAUD_MAX) )
return -EPERM;
// Calculate the value to be stored in Devisor Latch Register
baud_value=REF_FREEQUENCY/(16*baud_rate);
// baud_value(devisor value)
// Set th value in LS register
outb( (baud_value & 0x00ff), baseaddr);
while(count>0) // Wait for some time
count--; // Set MS DLAB register
count=10000000; // Wait for sometime
while(count>0)
count--;
outb(lcr_val,LCR); // Set frame format to default value
break;
/*----------------------------------------------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------------------------------------------*/
case UART_GETBAUD:
lcr_val=inb(LCR); // Store current LCR information
outb(DLABSET,LCR); // Enable DLAB
ls=inb(baseaddr);
ms=inb(IER);
// Return baud rate
outb(lcr_val,LCR); // Restore LCR information
return REF_FREEQUENCY /(16*(ls|ms));
break;
/*----------------------------------------------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------------------------------------------*/
case UART_SETFRAME:
if( (!capable(CAP_SYS_ADMIN)) || (arg<5) || (arg>8) )
return -EPERM;
length=arg;
cur=inb(LCR); // Get the current value of LCR
new=cur & 0xfc; // Reset bit 1 of LCR
if(length == 5)
{
// bit 0: 0 bit 1: 0
outb( (new | 0x00),LCR);
break;
}
else if(length == 6)
{
// bit 0: 0 bit 1: 1
outb( (new | 0x01),LCR);
break;
}
else if(length == 7)
{
// bit 0: 1 bit 1: 0
outb( (new | 0x02),LCR);
break;
}
else
{
// bit 0: 1 bit 1: 1
outb( (new | 0x03),LCR);
break;
}
outb(cur,LCR); // Restore current LCR information
break;
/*----------------------------------------------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------------------------------------------*/
case UART_SETFIFO:
trig_level=arg;
if(trig_level == 1)
{
// Set bit 7 and bit 6 according to trigger level
outb(0x07,IIR);
// for 1 bit 0 = 0, bit 1 = 0 in FCR
fifo=1;
}
else if(trig_level == 4)
{
// Set bit 7 and bit 6 according to trigger level
outb(0x4f,IIR);
// for 4 bit 0 = 0, bit 1 = 0 in FCR
fifo=4;
}
else if(trig_level == 8)
{
// Set bit 7 and bit 6 according to trigger level
outb(0x8f,IIR);
// for 8 bit 0 = 1, bit 1 = 0 in FCR
fifo=8;
}
else if(trig_level == 14)
{
// Set bit 7 and bit 6 according to trigger level
outb(0xcf,IIR);
// for 14 bit 0 = 0, bit 1 = 0 in FCR
fifo=14;
}
break;
/*----------------------------------------------------------------------------------------------------------------*/
default:
return -ENOTTY;
}
return 0;
}
int UART_Close(struct inode *in, struct file *fi)
{
free_irq(UART_IRQ,0); // Free the acquired IRQ line
if(open_cnt == 1) // To free the page allocated
free_page(write_buffer);
printk("<1>In closing\n");
open_cnt--;
MOD_DEC_USE_COUNT; // Decrement the module count
return 1;
}
module_init(UART_Init); // Driver entry point
module_exit(UART_Cleanup); // Driver exit point
/*--------------------------------------------------------------------------------------------------------------------------
In Write, write head moves
In transmit, write tail moves
In read, tail moves
In recieve, read head moves
--------------------------------------------------------------------------------------------------------------------------*/
(app.c)
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#define IOCTL_MAGIC 'x'
#define UART_SETBAUD _IOW(IOCTL_MAGIC,1,sizeof(int))
#define UART_GETBAUD _IOR(IOCTL_MAGIC,2,sizeof(int))
#define UART_SETFRAME _IOW(IOCTL_MAGIC,3,sizeof(int))
#define UART_SETFIFO _IOW(IOCTL_MAGIC,4,sizeof(int))
int main()
{
int fd,no;
char *buf="Hello",buf1[50];
fd=open("/dev/UART",O_RDWR);
perror("Open call successs");
no=write(fd,buf,sizeof(buf));
printf("\n%d\n",no);
printf("After Writing %s\n",buf);
perror("Write call");
printf("Before %d \n",ioctl(fd,UART_GETBAUD,0));
ioctl(fd,UART_SETBAUD,1000);
printf("After sending %d\n",ioctl(fd,UART_GETBAUD,0));
//read(fd,buf1,sizeof(buf1));
//perror("Read call\n");
//printf("%s\n",buf1);
close(fd);
perror("Close call\n");
return 0;
}
|