- 论坛徽章:
- 0
|
首先贴出自己的仿照LDD3上写的TTY设备驱动的源码:
/*所有的模块代码都包含下面两行代码*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h> //printk
#include <linux/errno.h> //err
#include <linux/slab.h> //kmalloc()
#include <linux/tty.h> //tty_struct
#include <linux/tty_driver.h> //tty_driver
#include <linux/tty_flip.h> //tty_flip_buffer_push()
#include <asm/uaccess.h> //copy_to_user()
#include <linux/wait.h> //wait_queue_head_t
#include <asm/semaphore.h> //semaphore
#include <linux/timer.h> //timer
#include <linux/serial.h> //serial_struct
#define DRIVER_VERSION "v2.0"
#define TINY_TTY_MAJOR 240 //主设备号
#define TINY_TTY_MINORS 4 //设备数
#define DELAY_TIME HZ*2
struct tiny_serial{
struct tty_struct *tty; /*指向该设备的tty指针*/
int open_count; /*该端口被打开的次数*/
struct semaphore sem; /*锁住该结构*/
struct timer_list *timer; /*申明一个定时器结构体*/
int msr; //MODEM状态寄存器
int mcr; //MODEM控制寄存器
struct serial_struct serial; //定义串口寄存器结构体
wait_queue_head_t wait; //定义等待队列头
};
static struct tiny_serial *tiny_table[TINY_TTY_MINORS]; /*初始化所有的tiny_serial为0*/
static struct tty_driver *tiny_tty_driver; //定义一个tty_driver指针
//定时器处理函数
static void tiny_timer(unsigned long timer_data)
{
struct tiny_serial *tiny=(struct tiny_serial *)timer_data;
struct tty_struct *tty;
int i;
char data[1]={'t'};
int data_size=1;
if(!tiny) //设备为NULL
return;
tty=tiny->tty; //得到tty_struct
for(i=0;i<data_size;++i)
{
if((*tty).count>=TTY_FLIPBUF_SIZE)
tty_flip_buffer_push(tty); //将交替缓冲区中数据发送给用户
tty_insert_flip_char(tty,data[i],TTY_NORMAL); /*将准备发给用户的字符添加到交替缓冲区中*/
}
tty_flip_buffer_push(tty);
//重新设置定时器
tiny->timer->expires=jiffies+DELAY_TIME;
add_timer(tiny->timer);
}
static int tiny_open(struct tty_struct *tty,struct file *file)
{
struct tiny_serial *tiny;
int index;
tty->driver_data=NULL; //初始化保存私有数据的空间
index=tty->index; /*获得与tty指针相关的串口对象*/
tiny=tiny_table[index];
if(tiny==NULL)
{
/*第一次访问该设备,创建它*/
tiny=kmalloc(sizeof(*tiny),GFP_KERNEL);
if(!tiny)
return -ENOMEM;
init_MUTEX(&tiny->sem); /*把信号量sem的值设为1*/
tiny->open_count=0; /*初始化引用计数器*/
tiny_table[index]=tiny;
}
down(&tiny->sem); //获取信号量
tty->driver_data=tiny; /*在tty结构中保存上述结构*/
tiny->tty=tty;
/*保存设备被打开的次数*/
++tiny->open_count;
if(tiny->open_count==1)
{
//动态初始化定时器
tiny->timer->data=(unsigned long)tiny; /*初始化function和data*/
tiny->timer->function=tiny_timer;
init_timer(tiny->timer);
//指定触发的时间
tiny->timer->expires=jiffies+DELAY_TIME;//延时2秒
//加入系统定时器链表
add_timer(tiny->timer);
}
up(&tiny->sem);
return 0;
}
static void do_close(struct tiny_serial *tiny)
{
down(&tiny->sem); /*获取信号量*/
if(!tiny->open_count)
//端口从未被打开
goto exit;
--tiny->open_count;
if(tiny->open_count<=0)
{
/*最后一个用户已经将端口关闭*/
del_timer(tiny->timer); //在定时器没有到期前关闭
}
exit:
up(&tiny->sem); /*释放信号量*/
}
static void tiny_close(struct tty_struct *tty,struct file *file)
{
struct tiny_serial *tiny=tty->driver_data;
if(tiny)
do_close(tiny);
}
static int tiny_write(struct tty_struct *tty,const unsigned char *buffer,int count)
{
struct tiny_serial *tiny=tty->driver_data;
int i;
int retval=-EINVAL;
if(!tiny)
//驱动程序没有设置open成员
return -ENODEV;
down(&tiny->sem);
if(!tiny->open_count)
//端口未被打开
goto exit;
/*将数据写入内核调试日志,来伪装将数据发送出硬件端口*/
printk(KERN_DEBUG "%s - ",__FUNCTION__);
for(i=0;i<count;++i)
printk("%02x\n",buffer[i]);
printk("\n" ;
exit:
up(&tiny->sem);
return retval;
}
static int tiny_write_room(struct tty_struct *tty)
{
struct tiny_serial *tiny=tty->driver_data;
int room=-EINVAL;
if(!tiny)
return -ENODEV;
down(&tiny->sem);
if(!tiny->open_count)
goto exit; //端口没有被打开
/*计算设备中可用空间的大小*/
room=255;
exit:
up(&tiny->sem);
return room;
}
#define RELEVANT_IFLAG(iflag) ((iflag)&(IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
static void tiny_set_termios(struct tty_struct *tty, struct ktermios * old_termios)
{
unsigned int cflag;
cflag=tty->termios->c_cflag; //得到驱动程序的termios设置
/*检查以保证确实有参数要被改变*/
if(old_termios) //先检查是否是个合法的指针
{
if((cflag==old_termios->c_cflag)&&
(RELEVANT_IFLAG(tty->termios->c_iflag)==
RELEVANT_IFLAG(old_termios->c_iflag)))
{
printk(KERN_DEBUG"nothing to change...\n" ;
return;
}
}
/*获得字节大小*/
switch(cflag & CSIZE)
{
case CS5:
printk(KERN_DEBUG "- data bits=5\n" ;
break;
case CS6:
printk(KERN_DEBUG "- data bits=6\n" ;
break;
case CS7:
printk(KERN_DEBUG "- data bits=7\n" ;
break;
default:
case CS8:
printk(KERN_DEBUG "- data bits=8\n" ;
break;
}
/*判断奇偶*/
if(cflag& ARENB) //是否设置奇偶校验值
if(cflag& ARODD)
printk(KERN_DEBUG"- parity=odd\n" ;
else
printk(KERN_DEBUG"- parity=even\n" ;
else
printk(KERN_DEBUG"- parity=none\n" ;
/*确定需要的停止位*/
if(cflag & CSTOPB)
printk(KERN_DEBUG"- stop bits=2\n" ;
else
printk(KERN_DEBUG"- stop bits=1\n");
/*确定硬件流控制设置*/
if(cflag & CRTSCTS)
printk(KERN_DEBUG"-RTS/CTS is enabled\n");
else
printk(KERN_DEBUG"-RTS/CTS is disabled\n");
/*确定软件流控制*/
//如果实现了XON/XOFF,设置设备中开始及结束字符
if(I_IXOFF(tty)||I_IXON(tty))
{
unsigned char stop_char = STOP_CHAR(tty);
unsigned char start_char = START_CHAR(tty);
//如果实现了XON/XOFF
if(I_IXOFF(tty))
printk(KERN_DEBUG"- INBOUND XON/XOFF is enabled,XON=%2x,XOFF=%2x",start_char,stop_char);
else
printk(KERN_DEBUG"- INBOUND XON/XOFF is disabled");
//如果实现了OUTBOUND XON/XOFF
if(I_IXON(tty))
printk(KERN_DEBUG"- OUTBOUND XON/XOFF is enabled,XON=%2x,XOFF=%2x",start_char,stop_char);
else
printk(KERN_DEBUG"- OUTBOUND XON/XOFF is disabled");
}
//获得需要的波特率
printk(KERN_DEBUG"- baud rate = %d",tty_get_baud_rate(tty));
}
#define MCR_DTR 0x01
#define MCR_RTS 0x02
#define MCR_LOOP 0x04
#define MSR_CTS 0x08
#define MSR_CD 0x10
#define MSR_RI 0x20
#define MSR_DSR 0x40
static int tiny_tiocmget(struct tty_struct *tty,struct file *file)
{
struct tiny_serial *tiny=tty->driver_data;
unsigned int result=0;
unsigned int msr=tiny->msr;
unsigned int mcr=tiny->mcr;
result=((mcr&MCR_DTR) ? TIOCM_DTR : 0) | /*设置了DTR*/
((mcr&MCR_RTS) ? TIOCM_RTS : 0) | /*设置了RTS*/
((mcr&MCR_LOOP) ? TIOCM_LOOP : 0) | /*设置了LOOP*/
((msr&MSR_CTS) ? TIOCM_CTS : 0) | /*设置了CTS*/
((msr&MSR_CD) ? TIOCM_CAR : 0) | /*设置了Carrier detect*/
((msr&MSR_RI) ? TIOCM_RI : 0) | /*设置了Ring Indicator*/
((msr&MSR_DSR) ? TIOCM_DSR : 0); /*设置了DSR*/
return result;
}
static int tiny_tiocmset(struct tty_struct *tty,struct file *file,unsigned int set,unsigned int clear)
{
struct tiny_serial *tiny=tty->driver_data;
unsigned int mcr=tiny->mcr; //得到MODEM控制寄存器
if(set & TIOCM_RTS)
mcr |=MCR_RTS;
if(set & TIOCM_DTR)
mcr |=MCR_RTS;
if(clear & TIOCM_RTS)
mcr &=~MCR_RTS;
if(clear & TIOCM_DTR)
mcr &=~MCR_RTS;
/*设置设备中新的MCR值*/
tiny->mcr=mcr;
return 0;
}
static int tiny_read_proc(char *page,char **start,off_t off,int count,int *eof,void *data)
{
struct tiny_serial *tiny;
off_t begin=0;
int length=0;
int i;
length+=sprintf(page,"tinyserinfo:1.0 driver:%s\n",DRIVER_VERSION);
for(i=0;i<TINY_TTY_MINORS && length< AGE_SIZE;++i) //length不能超过一页
{
tiny=tiny_table[i];
if(tiny==NULL)
continue;
length+=sprintf(page+length,"%d\n",i);
if((length+begin)>(off+count)) //达到了读数据的范围
goto done;
if((length+begin)<off)
{
begin +=length;
length=0;
}
}
*eof=1;
done:
if(off>=(length+begin)) //判断读数据的位置还没有到off
return 0;
*start=page+(off-begin); //调整start
return (count<begin+length-off) ? count :begin+length-off;
}
static struct tty_operations serial_ops={
.open = tiny_open,
.close = tiny_close,
.write = tiny_write,
.write_room = tiny_write_room, /*该函数用于检测缓冲区的剩余空间*/
.set_termios= tiny_set_termios, /*当设备的termios设置发生改变时,该函数被调用*/
};
static int __init tiny_init(void)
{
int retval,i;
/*分配tty驱动程序*/
tiny_tty_driver=alloc_tty_driver(TINY_TTY_MINORS);
if(!tiny_tty_driver)
return -ENOMEM;
/*初始化tty驱动程序*/
tiny_tty_driver->owner = THIS_MODULE;
tiny_tty_driver->driver_name = "tiny_tty"; //在/proc/tty和sysfs中使用
tiny_tty_driver->name = "mytty"; //驱动程序节点的名字
tiny_tty_driver->major = TINY_TTY_MAJOR; //tty的主设备号
tiny_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;//可以被任何串行类驱动程序使用
tiny_tty_driver->subtype = SERIAL_TYPE_NORMAL;
tiny_tty_driver->flags = TTY_DRIVER_REAL_RAW;
tiny_tty_driver->init_termios = tty_std_termios;
tiny_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
tty_set_operations(tiny_tty_driver,&serial_ops); /*复制到tty_driver对应的函数指针*/
tiny_tty_driver->read_proc=tiny_read_proc; /*proc*/
tiny_tty_driver->tiocmget=tiny_tiocmget; /*获得特定tty设备当前的线路设置*/
tiny_tty_driver->tiocmset=tiny_tiocmset; /*为特定的tty设备设置当前线路*/
/*注册tty驱动程序*/
retval=tty_register_driver(tiny_tty_driver);
if(retval)
{
printk(KERN_DEBUG"failed to register tiny tty driver:\n");
put_tty_driver(tiny_tty_driver); /*注册不成功,该函数负责清空tty_driver结构*/
return retval;
}
/*注册所有的tty设备(虚拟)*/
for(i=0;i<TINY_TTY_MINORS;++i)
tty_register_device(tiny_tty_driver,i,NULL);
printk(KERN_DEBUG"Tiny TTY driver register success:\n");
return retval;
}
static void __exit tiny_exit(void)
{
struct tiny_serial *tiny;
int i;
/*注销驱动程序*/
for(i=0;i<TINY_TTY_MINORS;++i)
tty_unregister_device(tiny_tty_driver,i);
tty_unregister_driver(tiny_tty_driver);
for(i=0;i<TINY_TTY_MINORS;++i) //依次遍历每一个tty设备
{
tiny=tiny_table[i]; //得到tty设备
if(tiny)
{
//关闭端口
while(tiny->open_count)
do_close(tiny);
kfree(tiny); //释放设备内存空间
tiny_table[i]=NULL;
}
}
}
module_init(tiny_init);
module_exit(tiny_exit);
MODULE_AUTHOR("chenqi");
MODULE_LICENSE("GPL");
注:我的VM内核是Linux2.6.18,我的ARM开发板的内核是linux2.6.22,另外在 /dev/ 中并没有mytty0设备文件。
在我将编译好的模块加载进ARM开发板内核时,出现如下的错误:
kobject_add failed for mytty0 with -EEXIST, don't try to register things with the same name in the same directory.
[<c002fde8>] (dump_stack+0x0/0x14) from [<c0178b58>] (kobject_shadow_add+0x190/0x1ac)
[<c01789c8>] (kobject_shadow_add+0x0/0x1ac) from [<c0178b88>] (kobject_add+0x14/0x1
r7:00000000 r6:c3e21e68 r5:c3e21e00 r4:c3e21e00
[<c0178b74>] (kobject_add+0x0/0x1 from [<c01ba628>] (device_add+0x88/0x53c)
[<c01ba5a0>] (device_add+0x0/0x53c) from [<c01baaf8>] (device_register+0x1c/0x20)
[<c01baadc>] (device_register+0x0/0x20) from [<c01baf64>] (device_create+0x7c/0xa0)
r4:c3e21e00
[<c01baeec>] (device_create+0x4/0xa0) from [<c019a7c8>] (tty_register_device+0xbc/0xd
r8:00000000 r7:c3dade58 r6:bf001360 r5:00000000 r4:c06ee600
[<c019a70c>] (tty_register_device+0x0/0xd from [<bf003118>] (tiny_init+0x118/0x15c [mytty])
[<bf003000>] (tiny_init+0x0/0x15c [mytty]) from [<c0063854>] (sys_init_module+0x1424/0x1514)
r5:bf001360 r4:00000000
[<c0062430>] (sys_init_module+0x0/0x1514) from [<c002bea0>] (ret_fast_syscall+0x0/0x2c)
Tiny TTY driver register success
但是在我的VM中编译加载模块并没有问题,所以我自己的想法是因为内核版本的问题才导致加载出现问题,但是我不知道应该怎么去修改,还望大神指教!!! |
|