LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   device driver for UART (write illegal seek - error) (https://www.linuxquestions.org/questions/programming-9/device-driver-for-uart-write-illegal-seek-error-453478/)

adit_2g 06-10-2006 01:07 PM

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;
}


All times are GMT -5. The time now is 04:18 PM.