![](https://img-my.csdn.net/uploads/201204/11/1334134320_7865.png)
1)cd linux-source-3.0.0
2)lsusb /*查看所有连接到系统的USB设备*/
3)拔掉USB设备,然后再运行一遍lsusb命令,这样就可以确定以上哪条信息是针对你的新硬件的了。
Bus 002 Device 004: ID 1e3d:2093 /*我的硬件识别信息*/
其中ID 1e3d:2093这个信息对我们很有用处,我们需要用它来查找内核中与硬件匹配的信息。1e3d代表的是厂商ID,就是哪家厂商。2093是硬件ID。
下面开始用1e3d搜索内核源码树:
4)grep -i -R-l 0403 drivers
drivers/usb/serial/ftdi_sio.mod.o
drivers/usb/serial/ftdi_sio.ko
drivers/usb/serial/ftdi_sio.mod.c
drivers/watchdog/pcwd_pci.ko
drivers/watchdog/pcwd_pci.mod.c
drivers/watchdog/pcwd_pci.mod.o
drivers/bluetooth/btusb.ko
drivers/bluetooth/btusb.mod.c
drivers/bluetooth/btusb.mod.o
drivers/media/rc/keymaps/rc-hauppauge.c
drivers/media/rc/keymaps/rc-dib0700-rc5.c
drivers/media/dvb/dvb-usb/dvb-usb-vp702x.mod.c
drivers/media/dvb/dvb-usb/nova-t-usb2.c
drivers/media/dvb/dvb-usb/dvb-usb-vp702x.ko
drivers/media/dvb/dvb-usb/dvb-usb-vp702x.mod.o
该命令执行完后,会在屏幕上显示若干条以.data .c .h等为结尾的文件,比如drivers/usb/serial/ftdi_sio.ko不用看最后一部分,前三个目录名就可以确定这是个USB串口设备。同样的判断方法,我们就可以确定我们需要的内核文件了。以防万一,我们进入这个文件中,USB驱动告诉内核它们支持哪些谁被,以便内核可以把驱动绑定到设备上。一般在一个结构体变量中列出制造商ID和设备ID。如果我们设备的制造商ID和设备ID在里面的话,说明这个驱动支持我们的硬件设备。
cd linux-3.0.0 /*进入内核文件中*/
find –type f –name Makefile | xargs grep XXXXX/*会显示一个以CONFIG_为前缀的字段*/
找到这个字段后,返回内核Makefile文件中,使用内核配置工具menuconfig,搜索这个字段。最后在该程序菜单中相应位置启动这个驱动。
- struct proc_dir_entry*create_proc_read_entry(constchar*name,mode_t mode,
- struct proc_dir_entry*base,
- read_proc_t*read_proc,void*data);
- /*创建proc文件接口
- **name:要创建的文件名称
- **mode:该文件的保护码,0表示系统默认值
- **base:指定文件所在目录,base如果为NULL,表示在proc根目录下创建该文件
- **read_proc:该文件的read_proc函数,读取该文件时调用
- **data:传递给read_proc的参数
- */
- int(*read_proc)(char*page,char**start,off_t offset,intcount,int*eof,void*data);
- /*
- **page:指针指向用来写入数据的缓冲区
- **start:返回实际的数据写到内存页得哪个位置
- **offset,count:和read方法一样
- **eof:指向一个整形数,当没有数据可返回时,驱动程序必须设置这个参数
- **data:提供给驱动程序的专用数据指针,可用于内部记录
- */
- 返回值:必须返回存放到内存页缓冲区的字节数,*eof和*start也属于返回值
- void remove_proc_entry(constchar*name,struct proc_dir_entry*base)//如果删除已卸载模块,内核会崩溃
- /*移除proc文件
- **name:文件名
- **base:目录,和创建前面一样
- */
Seq_file 的实现基于proc文件。使用 Seq_file ,用户必须抽象出一个链接对象,然后可以依次遍历这个链接对象。这个链接对象可以是链表,数组,哈希表等等。
编程接口
Seq_file 必须实现四个操作函数:start(), next(), show(), stop()。
struct seq_operations {
void * ( * start ) ( struct seq_file * m , loff_t * pos ) ;
void ( * stop ) ( struct seq_file * m , void * v ) ;
void * ( * next ) ( struct seq_file * m , void * v , loff_t * pos ) ;
int ( * show ) ( struct seq_file * m , void * v ) ;
} ;
start():
stop():
next():
show():
对遍历对象进行操作的函数。主要是调用seq_printf(), seq_puts()之类的函数,打印出这个对象节点的信息。
下图描述了 seq_file 函数对一个链表的遍历。
2、重要的数据结构
除了struct seq_operations以外,另一个最重要的数据结构是struct seq_file :
struct seq_file {
char *buf;
size_t size;
size_t from;
size_t count;
loff_t index;
u64 version;
struct mutex lock;
const struct seq_operations *op;
void *private;
};
该结构会在seq_open函数调用中分配,然后作为参数传递给每个 seq_file 的操作函数。Privat变量可以用来在各个操作函数之间传递参数。
3、 Seq_file 使用示例:
# include /* for use of init_net*/
# include /* We're doing kernel work */
# include /* Specifically, a module */
# include /* Necessary because we use proc fs */
# include /* forseq_file*/
# define PROC_NAME "my_seq_proc"
MODULE_LICENSE ( "GPL" ) ;
static void * my_seq_start ( struct seq_file * s , loff_t * pos )
{
static unsigned long counter = 0 ;
printk ( KERN_INFO "Invoke start\n" ) ;
/* beginning a new sequence ? */
if ( * pos = = 0 )
{
/* yes => return a non null value to begin the sequence */
printk ( KERN_INFO "pos == 0\n" ) ;
return & counter ;
}
else
{
/* no => it's the end of the sequence, return end to stop reading */
* pos = 0 ;
printk ( KERN_INFO "pos != 0\n" ) ;
return NULL ;
}
}
static void * my_seq_next ( struct seq_file * s , void * v , loff_t * pos )
{
unsigned long * tmp_v = ( unsigned long * ) v ;
printk ( KERN_INFO "Invoke next\n" ) ;
( * tmp_v ) + + ;
( * pos ) + + ;
return NULL ;
}
static void my_seq_stop ( struct seq_file * s , void * v )
{
printk ( KERN_INFO "Invoke stop\n" ) ;
/* nothing to do, we use a static value in start() */
}
static int my_seq_show ( struct seq_file * s , void * v )
{
printk ( KERN_INFO "Invoke show\n" ) ;
loff_t * spos = ( loff_t * ) v ;
seq_printf ( s , "%Ld\n" , * spos ) ;
return 0 ;
}
static struct seq_operations my_seq_ops = {
. start = my_seq_start ,
. next = my_seq_next ,
. stop = my_seq_stop ,
. show = my_seq_show
} ;
static int my_open ( struct inode * inode , struct file * file )
{
return seq_open ( file , & my_seq_ops ) ;
} ;
static struct file_operations my_file_ops = {
. owner = THIS_MODULE ,
. open = my_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = seq_release
} ;
int init_module ( void )
{
struct proc_dir_entry * entry ;
entry = create_proc_entry ( PROC_NAME , 0 , init_net . proc_net ) ;
if ( entry ) {
entry - > proc_fops = & my_file_ops ;
}
printk ( KERN_INFO "Initialze my_seq_proc success!\n" ) ;
return 0 ;
}
/**
* This function is called when the module is unloaded.
*
*/
void cleanup_module ( void )
{
remove_proc_entry ( PROC_NAME , init_net . proc_net ) ;
printk ( KERN_INFO "Remove my_seq_proc success!\n" ) ;
}
该程序在/proc/net下注册一个my_seq_proc文件。
1. 引用空指针
Unable to handle kernel NULL pointer dereference at virtual address 00000000
printing eip:
d083a064
Oops: 0002 [#1]
SMP
CPU: 0
EIP: 0060:[<d083a064>] Not tainted
EFLAGS: 00010246 (2.6.6)
EIP is at faulty_write+0x4/0x10 [faulty]
eax: 00000000 ebx: 00000000 ecx: 00000000 edx: 00000000
esi: cf8b2460 edi: cf8b2480 ebp: 00000005 esp: c31c5f74
ds: 007b es: 007b ss: 0068
Process bash (pid: 2086, threadinfo=c31c4000 task=cfa0a6c0)
Stack: c0150558 cf8b2460 080e9408 00000005 cf8b2480 00000000 cf8b2460 cf8b2460
fffffff7 080e9408 c31c4000 c0150682 cf8b2460 080e9408 00000005 cf8b2480
00000000 00000001 00000005 c0103f8f 00000001 080e9408 00000005 00000005
Call Trace:
[<c0150558>] vfs_write+0xb8/0x130
[<c0150682>] sys_write+0x42/0x70
[<c0103f8f>] syscall_call+0x7/0xb
Code: 89 15 00 00 00 00 c3 90 8d 74 26 00 83 ec 0c b8 00 a6 83 d0
这个错误消息比较明显的,指到了空指针,位置在faulty_write 后 四个字节。
2. 堆栈被破坏
EIP: 0010:[<00000000>]
Unable to handle kernel paging request at virtual address ffffffff
printing eip:
ffffffff
Oops: 0000 [#5]
SMP
CPU: 0
EIP: 0060:[<ffffffff>] Not tainted
EFLAGS: 00010296 (2.6.6)
EIP is at 0xffffffff
eax: 0000000c ebx: ffffffff ecx: 00000000 edx: bfffda7c
esi: cf434f00 edi: ffffffff ebp: 00002000 esp: c27fff78
ds: 007b es: 007b ss: 0068
Process head (pid: 2331, threadinfo=c27fe000 task=c3226150)
Stack: ffffffff bfffda70 00002000 cf434f20 00000001 00000286 cf434f00 fffffff7
bfffda70 c27fe000 c0150612 cf434f00 bfffda70 00002000 cf434f20 00000000
00000003 00002000 c0103f8f 00000003 bfffda70 00002000 00002000 bfffda70
Call Trace:
[<c0150612>] sys_read+0x42/0x70
[<c0103f8f>] syscall_call+0x7/0xb
Code:Bad EIP value.
这个错误信息比较隐晦的。 说的是,找不到一个虚拟地址。 EIP一看就是个乱七八糟的值。
call trace不完整,只指示到了 sys_read。
造成错误的源代码是:
ssize_t faulty_read(struct file *filp, char __user *buf,size_t count, loff_t *pos)
{
int ret;
char stack_buf[4];
/* Let's try a buffer overflow */
memset(stack_buf, 0xff, 20);
if (count > 4)
count = 4; /* copy 4 bytes to the user */
ret = copy_to_user(buf, stack_buf, count);
if (!ret)
return count;
return ret;
}