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. |