LinuxQuestions.org
Share your knowledge at the LQ Wiki.
Go Back   LinuxQuestions.org > Blogs > adit_2g
User Name
Password

Notices

Rate this Entry

device driver problems

Posted 06-10-2006 at 12:57 PM by adit_2g

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






Posted in Uncategorized
Views 1062 Comments 0
« Prev     Main     Next »

  



All times are GMT -5. The time now is 11:13 AM.

Main Menu
Advertisement

My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration