ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
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?
-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.
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.
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) */
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?
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.
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.
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.
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.
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?
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?
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:
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.