LinuxQuestions.org
Visit Jeremy's Blog.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 05-26-2004, 03:49 AM   #1
jamespayne
LQ Newbie
 
Registered: May 2004
Posts: 18

Rep: Reputation: 0
How to use serial ports in kernel?


Hi there,

I am a newbie to kernel,So please be patient if I have asked something silly. Recently, I am trying to use serial port in my kernel module.
In user space, this is simple because we can open("/dev/ttyS0",......).
However, as a beginner, I don't know how to user serial port in kernel module.

I have tried to open "/dev/ttyS0" by filp_open():
struct file* filp=filp_open("/dev/ttyS0",O_RDWR,S_IRWXU|S_IRWXG|S_IRWXO);

I can correctly get the serial port's device major number from:
filp->f_dentry->d_inode->i_rdev

However, when I attempting to send data using:
if{(flag=filp->f_op->write(filp,buf,size))<0) printk(KERN_DEBUG,"failed to write");

It failed. How could this happened? What should I do if I would like to enable my kernel module to use serial ports?

Any suggestion would be welcome!
 
Old 05-26-2004, 01:36 PM   #2
infamous41md
Member
 
Registered: Mar 2003
Posts: 804

Rep: Reputation: 30
http://www.beyondlogic.org/serial/serial.htm
http://tldp.org/HOWTO/Serial-HOWTO.html

-in short:
1)allocate your range with request_range(). on nearly every comp, serial port 1 starts at 0x3f8, port 2 0x2f8.
2)allocate your irq, port 1: 4, port 2: 3
3)read and write to your registers
i just recently wrote a driver for an infrared remote that uses serial port, it's not too much code, and found below on my website.
 
Old 05-26-2004, 10:58 PM   #3
jamespayne
LQ Newbie
 
Registered: May 2004
Posts: 18

Original Poster
Rep: Reputation: 0
Oh great! I will try it later.
But I have another question, your solution looks like to write a driver. Would it conflict with the original driver builded in linux kernel?
By the way, I am not able to access your site. Would you please send me a copy to peppeng@hotmail.com? Just the lines to open/write the serial port are ok if the whole one is not very convenient to give me.
 
Old 05-27-2004, 03:54 AM   #4
jamespayne
LQ Newbie
 
Registered: May 2004
Posts: 18

Original Poster
Rep: Reputation: 0
I have wrote the following codes to just send data through serial port while there is another tool to read the data. But when the program stopped when entering the first call to outb() with the "Segmentation Faults".
Here is my simple codes:

#include <stdio.h>
#include <asm/io.h>

#define PORT1 0x3F8 /* Port Address Goes Here */
#define INTVECT 0x04 /* Com Port's IRQ here (Must also change PIC setting) */

#define outb_s(port,command) outb(command,port)
#define inb_s(port) inb(port)

/* Defines Serial Ports Base Address */
/* COM1 0x3F8 */
/* COM2 0x2F8 */
/* COM3 0x3E8 */
/* COM4 0x2E8 */

int count = 0;
char ch;
char buffer[]="\xc0Hello Serial!\xc0";

int main(void)
{
outb_s(PORT1 + 1 , 0); /* Turn off interrupts - Port1 */

/* PORT 1 - Communication Settings */

outb_s(PORT1 + 3 , 0x80); /* SET DLAB ON */
outb_s(PORT1 + 0 , 0x0C); /* Set Baud rate - Divisor Latch Low Byte */
/* Default 0x03 = 38,400 BPS */
/* 0x01 = 115,200 BPS */
/* 0x02 = 57,600 BPS */
/* 0x06 = 19,200 BPS */
/* 0x0C = 9,600 BPS */
/* 0x18 = 4,800 BPS */
/* 0x30 = 2,400 BPS */
outb_s(PORT1 + 1 , 0x00); /* Set Baud rate - Divisor Latch High Byte */
outb_s(PORT1 + 3 , 0x03); /* 8 Bits, No Parity, 1 Stop Bit */
outb_s(PORT1 + 2 , 0xC7); /* FIFO Control Register */
outb_s(PORT1 + 4 , 0x0B); /* Turn on DTR, RTS, and OUT2 */

outb_s(0x21,(inb_s(0x21) & 0xEF)); /* Set Programmable Interrupt Controller */
/* COM1 (IRQ4) - 0xEF */
/* COM2 (IRQ3) - 0xF7 */
/* COM3 (IRQ4) - 0xEF */
/* COM4 (IRQ3) - 0xF7 */

outb_s(PORT1 + 1 , 0x01); /* Interrupt when data received */

do {

outb_s(PORT1, buffer[count]);

} while (count++<sizeof(buffer));

outb_s(PORT1 + 1 , 0); /* Turn off interrupts - Port1 */
return 0;
}
 
Old 05-27-2004, 03:57 AM   #5
jamespayne
LQ Newbie
 
Registered: May 2004
Posts: 18

Original Poster
Rep: Reputation: 0
These codes are just for test, so I didn't put them under the kernel.
 
Old 05-27-2004, 12:04 PM   #6
infamous41md
Member
 
Registered: Mar 2003
Posts: 804

Rep: Reputation: 30
the serial driver, by default, is built into the kernel and cannot be unloaded. u either need to rebuild the kernel and modularize it, or use the setserial program to change the i/o address and irq that the driver uses. this second solution is somewhat of a hack, as what u do is just redirect the driver to use bogus i/o address/irq line. as to the segfault, i suggest u read the materials i gave u. u need to call ioperm() or iopl() to gain access to serial ports from user space. my site is up and working fine, not sure why u couldnt connect? are u using a proxy that's filtering or something?
 
Old 05-27-2004, 07:38 PM   #7
jamespayne
LQ Newbie
 
Registered: May 2004
Posts: 18

Original Poster
Rep: Reputation: 0
Is there any way to use the existing driver in stead of write one when I need to use serial port in my kernel module?
 
Old 05-27-2004, 08:31 PM   #8
infamous41md
Member
 
Registered: Mar 2003
Posts: 804

Rep: Reputation: 30
of course not. your driver needs to have exclusive access to the i/o ports it is using. if u leave the regular serial module installed, any user space program that uses serial ports could start writing to your devices registers freely. u need to claim your region with request_region(). here is some example code from my driver:
Code:
static int ir_init(void)
{
    int ret;

    SET_MODULE_OWNER(&fops);

    //request io region
    if( !request_region(mird_ioaddr, 8, MOD_NAME) )
        PDBG("error requesting addr %x\n", mird_ioaddr);

    //initialize i/o registers
    init_port(mird_ioaddr);
...

//  initializes an i/o address's UART ports by:
//  turning off interrupts, zero regs,
//  turning on power, finding if reciever is active hi or lo
//@param base i/o address of base register
static void init_port(u_int base)
{
    u_long    flags = 0;
    u_char  status = 0;
    
    LOCK(flags);
    
    //turn off dlab
    NODLAB(base);
    
    //disable interrupts
    outb((~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)),
            base+UART_IER);
            
    /* Clear registers. */
    inb(base+UART_LSR);
    inb(base+UART_RX);
    inb(base+UART_IIR);
    inb(base+UART_MSR);
    
    //power source
    outb(UART_MCR_RTS|UART_MCR_OUT2, base+UART_MCR);

    UNLOCK(flags);

    //find if active high or low
    //but wait one second for power supply
    sleep_for(1);
    status = inb(base+UART_MSR);

    if( status & UART_MSR_DCD ){
        PDBG("reciever active low\n");
        mird_hilo = MIRD_LOW;   //1
    }else{
        PDBG("receiver active high\n");
        mird_hilo = MIRD_HIGH;  //0
    }

}

Last edited by infamous41md; 05-27-2004 at 08:33 PM.
 
Old 05-31-2004, 10:52 PM   #9
jamespayne
LQ Newbie
 
Registered: May 2004
Posts: 18

Original Poster
Rep: Reputation: 0
one more question please,
My module can send data through serial port correctly now.
however, there is a problem when I am attempting to receive.
2 methods were used:
(1) read the serial port without interrupt
////////////////////////////////////////////
int receiver(void){
int c=0,count=0;
char temp[BUFFERSIZE];
do { c = inb(PORT1 + 5);
if (c & 1) {
temp[count] = inb(PORT1);
printk("%c(%02x) ",temp[count],temp[count]);
count++;
if (count == BUFFERSIZE-1) {count = 0;}
}
}while (c & 1);
return 0;
}
int init_module(void)
{
init_com();
receiver();
return 0;
}
/////////////////////////////////////////////

I write some chars into serial port, but I can just receive the first one in my module.
I don't know how to figure this problem.

(2) read in interrupt
////////////////////////////////////////////
void receiver(int irq, void* dev_id, struct pt_regs* regs){
int c=0,count=0;
char temp[BUFFERSIZE];
printk("\n<1>we receive:\n");
do { c = inb(PORT1 + 5);
if (c & 1) {
temp[count] = inb(PORT1);
printk("%c(%02x) ",temp[count],temp[count]);
count++;
if (count == BUFFERSIZE-1) {count = 0;}
}
}while (c & 1);
}
int init_module(void)
{
int flag=0;
init_com();
if((flag=request_irq(Serial_IRQ,receiver,SA_SHIRQ,Serial_NAME,NULL))!=0)
printk("<1>\nFailed to request irq\n");
else printk("<1>\nRequest irq ok\n");
return 0;
}
////////////////////////////////////////////

It doesn't work, because if my module is insert,
/proc/interrupt shows that my system can no longer get interrupt on irq4.
Thus the interrup handler cannot read the serial port.
What's worse, if I use my user space program to communicate through serial port after inserting my module.
The system will be down. Data in stack will be shown, plus some "EIP:0060:[<d090b290>] Not tainted",
"EFLAGS:00010202" and "Bad EIP Value" errors.
Could you help me with some tips and solutions?

Last edited by jamespayne; 05-31-2004 at 11:13 PM.
 
Old 05-31-2004, 11:03 PM   #10
infamous41md
Member
 
Registered: Mar 2003
Posts: 804

Rep: Reputation: 30
first off, u should have looked at the serial modules in the kernel. u need to be using the symbolic defines, ur code would be much simpler to read.
/include/linux/serial_reg.h

recode ur module to use those constants and i'll look at it. and when u request a shared IRQ u CANNOT pass the final p ointer as NULL, that pointer is used to identify what irq handler to free when u call free_irq(). when you post code, USE CODE TAGS SO THE INDENTATION IS PRESERVED. read the link in my sig for how to use code tags. in your handler, u need to clear the interrupt pending bit in the IIR register, if u dont clear the bit, no more interrupts will be generated. and finally, you're writing kernel code, if u can't figure out how to debug faults on your own... i suggest reading this book: http://www.xml.com/ldd/chapter/book/ , and also read the links i gave u.
 
Old 06-01-2004, 03:38 AM   #11
jamespayne
LQ Newbie
 
Registered: May 2004
Posts: 18

Original Poster
Rep: Reputation: 0
I have modified some part of my code according to your suggestion, and
I dont have a system crash any longer when interrupt occurs.
However, there is one thing that doesnt change.
I still have only ONE charactor received through my interrupt handler.
I list all my code below
Code:
#include <linux/serial_reg.h>	// for macros related to serial register

#define PORT1 0x3F8  /* Port Address Goes Here */

#define outb_s(port,command) outb(command,port)
#define inb_s(port) inb(port)

#define SIRQ	4
#define SNAME	"myserial"

  /* Defines Serial Ports Base Address */
  /* COM1 0x3F8                        */
  /* COM2 0x2F8			       */
  /* COM3 0x3E8			       */
  /* COM4 0x2E8			       */

#define BUFFERSIZE 100

char buffer_rx[BUFFERSIZE];

int init_main(void){

	outb_s(PORT1 + UART_IER , 0);        /* Turn off interrupts - Port1 */

 /*         PORT 1 - Communication Settings         */

	outb_s(PORT1 + UART_LCR , UART_LCR_DLAB);  /* SET DLAB ON */

	outb_s(PORT1 + UART_DLL , 0x0C);

	/* Set Baud rate - Divisor Latch Low Byte */
	/*         0x01 = 115,200 BPS */
	/*         0x02 =  57,600 BPS */
	/*         0x06 =  19,200 BPS */
	/*         0x0C =   9,600 BPS */
	/*         0x18 =   4,800 BPS */
	/*         0x30 =   2,400 BPS */

	outb_s(PORT1 + UART_DLM , 0x00);  /* Set Baud rate - Divisor Latch High Byte */
	
	outb_s(PORT1 + UART_LCR , UART_LCR_WLEN8);  /* 8 Bits, No Parity, 1 Stop Bit */
	
	outb_s(PORT1 + UART_FCR , UART_FCR_TRIGGER_14|UART_FCR_ENABLE_FIFO|UART_FCR_CLEAR_RCVR|UART_FCR_CLEAR_XMIT);  
	/* FIFO Control Register */
	
	outb_s(PORT1 + UART_MCR , UART_MCR_OUT2|UART_MCR_RTS|UART_MCR_DTR);  
	/* Turn on DTR, RTS, and OUT2 */
	
	outb_s(PORT1 + UART_LCR , inb_s(PORT1 + UART_LCR)&(~UART_LCR_DLAB));  
	/* SET DLAB OFF */
	
	outb_s(PORT1 + UART_IER , UART_IER_RDI);  
	/* Interrupt when data received */

	return 0;
}

#ifdef _INTER
void receiver(int irq, void* dev_id, struct pt_regs* regs){
	outb_s(PORT1 + UART_IIR, (inb_s(PORT1 + UART_IIR)&(~UART_IIR_NO_INT)));	/* interrupts pending */
#else
int receiver(void){
#endif
	int c=0,count=0;
	char temp[BUFFERSIZE];
	printk("\n<1>we receive:\n");
	do { c = inb(PORT1 + UART_LSR);
		if (c & 1) {
			temp[count] = inb(PORT1);
			printk("%c(%02x) ",temp[count],temp[count]);
			count++;
			if (count == BUFFERSIZE-1) {count = 0;}
		}
	}while (c & 1);
#ifdef _INTER
	outb_s(PORT1 + UART_IIR, UART_IIR_NO_INT);	/* No interrupts pending */
#else
	return 0;
#endif
}

int init_module(void)
{
	int flag=0;
	printk("<1>\nPepp: Serial Module Initiating...\n");
	init_main();
#ifdef _INTER
	if((flag=request_irq(SIRQ,receiver,0,SNAME,NULL))!=0){
		printk("<1>\nFailed to request irq\n");
	}
	else{
		printk("<1>\nRequest irq ok\n");
	}
#else
	receiver();
#endif
	return 0;
}
void cleanup_module(void)
{
#ifdef _INTER
	free_irq(SIRQ,SNAME);
#endif
	printk("<1>Pepp: Good-bye, kernel!\n");
}

Last edited by jamespayne; 06-01-2004 at 03:44 AM.
 
Old 06-01-2004, 04:07 AM   #12
jamespayne
LQ Newbie
 
Registered: May 2004
Posts: 18

Original Poster
Rep: Reputation: 0
System crash again!
just server minutes after removing my module.
server log says:
Unable to handle kernel paging request at virtual address d089a2e5
.......
Would it be caused by free_irq fail?
 
Old 06-01-2004, 06:52 AM   #13
jamespayne
LQ Newbie
 
Registered: May 2004
Posts: 18

Original Poster
Rep: Reputation: 0
It seems that every time I remove my module, system will down if any use serial port again.
I think after request_irq & free_irq, the original IRQ4 interrupt handler is lost. If another serial port interrupt occurs, the system cannot find a handler, thus it crashes.
Am I right? And how to preserve the original handler?
 
Old 06-01-2004, 12:52 PM   #14
infamous41md
Member
 
Registered: Mar 2003
Posts: 804

Rep: Reputation: 30
1) you dont write to IIR to clear the bit, all u do is this:
while( !(inb(mird_ioaddr+UART_IIR) & UART_IIR_NO_INT) );
2)you are NOT passing the correct arg to free_irq or request_irq:
Code:
(request_irq(mird_irq, irq_handler, SA_INTERRUPT|SA_SHIRQ, MOD_NAME,
                            &mird_id)
free_irq(mird_irq, &mird_id);
 
Old 06-02-2004, 12:03 AM   #15
jamespayne
LQ Newbie
 
Registered: May 2004
Posts: 18

Original Poster
Rep: Reputation: 0
According to Linux Device Drivers 2ed
Code:
int request_irq(unsigned int irq,
void (*handler)(int, void *, struct pt_regs *),
unsigned long flags,
const char *dev_name,
void *dev_id);
dev_id and SA_SHIRQ are used for shared interrupt lines.
need I support it?
In my simple code, I even dont know how to set the dev_id.

By the way, I am studing kdb now. Is it the optimal debugger in kernel?
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Can only use one of my serial ports steaktc Linux - Hardware 1 11-23-2005 06:07 AM
Can't get serial ports after upgraded Kernel longnam Slackware 2 06-11-2005 04:51 AM
need extra serial ports iansworld Linux - Hardware 0 02-17-2005 02:27 PM
Serial ports KenHorse Programming 2 12-27-2004 08:49 AM
Kernel config/compilation (serial ports, lirc, apm) Hex29A Debian 2 02-07-2004 05:55 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 10:34 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
Open Source Consulting | Domain Registration