Linux下的简单字符驱动--改自宋宝华《Linux驱动开发详解第2版》在kernel2.6.38通过

Linux下的简单字符驱动--改自宋宝华《Linux驱动开发详解第2版》在kernel2.6.38通过,
实验过程:在fedora9虚拟机上完成交叉编译,生成mini6410_globalmem.ko, 复制到tiny6410开发板上。
可以用insmod及rmmod 添加删除

/* globalmem driver as an example of char device drivers

   mini6410_globalmem.c
    
The
 
initial
 
developer
 
of
 
the
 
original
 
code
 
is
 
Baohua
 
Song
    
.
 
All
 
Rights
 
Reserved.
======================================================================*/
#include
 

#include
 

#include
 

#include
 

#include
 

#include
 

#include
 

#include
 

#include
 

#include
 

#include
 

#include
 

#include
 

#define
 
GLOBALMEM_SIZE 
0x1000
 
/*全局内存最大4K字节*/
#define
 
MEM_CLEAR 
0x1
  
/*清0全局内存*/
#define
 
GLOBALMEM_MAJOR 
233
    
/*预设的globalmem的主设备号*/
struct
 
class
 
*
my_class;
static
 
globalmem_major 
=
 
GLOBALMEM_MAJOR;
/*globalmem设备结构体*/
struct
 
globalmem_dev
                                     
{
                                                        
  
struct
 
cdev
 
cdev
;
 
/*cdev结构体*/
                       
  
unsigned
 
char
 
mem
[
GLOBALMEM_SIZE];
 
/*全局内存*/
     
  
struct
 
semaphore
 
sem
;
   
};
struct
 
globalmem_dev
 
*
globalmem_devp;
 
/*设备结构体指针*/
/*文件打开函数*/
int
 
globalmem_open(
struct
 
inode
 
*
inode
,
 
struct
 
file
 
*
filp
)
{
  
/*将设备结构体指针赋值给文件私有数据指针*/
  
filp
->
private_data 
=
 
globalmem_devp;
  
return
 
0
;
}
/*文件释放函数*/
int
 
globalmem_release(
struct
 
inode
 
*
inode
,
 
struct
 
file
 
*
filp
)
{
  
return
 
0
;
}
/*
 
ioctl设备控制函数
 
after
 
kernel2.6.36
 
remove
 
ioctl,add
 
unlocked_ioctl*/
static
 
int
 
globalmem_unlocked_ioctl(
struct
 
inode
 
*
inodep
,
 
struct
 
file
 
*
filp
,
 
unsigned
  
int
 
cmd
,
 
unsigned
 
long
 
arg
)
{
  
struct
 
globalmem_dev
 
*
dev
 
=
 
filp
->
private_data;
//获得设备结构体指针
  
switch
 
(
cmd
)
  
{
    
case
 
MEM_CLEAR:
 
if
(
down_interruptible(&
dev
->
sem
))
  
return
 
-
ERESTARTSYS;
      
memset(
dev
->
mem
,
 
0
,
 
GLOBALMEM_SIZE);
      
 
up(&
dev
->
sem
);
      
printk(
KERN_INFO 
"globalmem
 
is
 
set
 
to
 
zero/n"
);
      
break
;
    
default
:
      
return
  
-
 
EINVAL;
  
}
  
return
 
0
;
}
/*读函数*/
static
 
ssize_t 
globalmem_read(
struct
 
file
 
*
filp,
 
char
 
__user 
*
buf,
 
size_t 
size,
  
loff_t 
*
ppos)
{
  
unsigned
 
long
 
p 
=
  
*
ppos;
  
unsigned
 
int
 
count 
=
 
size;
  
int
 
ret 
=
 
0
;
  
struct
 
globalmem_dev
 
*
dev 
=
 
filp->
private_data;
 
/*获得设备结构体指针*/
  
/*分析和获取有效的写长度*/
  
if
 
(
p 
>=
 
GLOBALMEM_SIZE)
    
return
 
count 
?
  
-
 
ENXIO:
 
0
;
  
if
 
(
count 
>
 
GLOBALMEM_SIZE 
-
 
p)
    
count 
=
 
GLOBALMEM_SIZE 
-
 
p;
  
if
(
down_interruptible(&
dev->
sem))
 
return
 
-
ERESTARTSYS;
  
/*内核空间->用户空间*/
  
if
 
(
copy_to_user(
buf,
 
(
void
*)(
dev->
mem 
+
 
p),
 
count))
  
{
    
ret 
=
  
-
 
EFAULT;
  
}
  
else
  
{
    
*
ppos 
+=
 
count;
    
ret 
=
 
count;
    
    
printk(
KERN_INFO 
"read
 
%d
 
bytes(s)
 
from
 
%d/n"
,
 
count,
 
p);
  
}
  
up(&
dev->
sem
);
  
return
 
ret;
}
/*写函数*/
static
 
ssize_t 
globalmem_write(
struct
 
file
 
*
filp,
 
const
 
char
 
__user 
*
buf,
  
size_t 
size,
 
loff_t 
*
ppos)
{
  
unsigned
 
long
 
p 
=
  
*
ppos;
  
unsigned
 
int
 
count 
=
 
size;
  
int
 
ret 
=
 
0
;
  
struct
 
globalmem_dev
 
*
dev 
=
 
filp->
private_data;
 
/*获得设备结构体指针*/
  
  
/*分析和获取有效的写长度*/
  
if
 
(
p 
>=
 
GLOBALMEM_SIZE)
    
return
 
count 
?
  
-
 
ENXIO:
 
0
;
  
if
 
(
count 
>
 
GLOBALMEM_SIZE 
-
 
p)
    
count 
=
 
GLOBALMEM_SIZE 
-
 
p;
    
  
if
(
down_interruptible(&
dev->
sem))
 
return
 
-
ERESTARTSYS;
  
/*用户空间->内核空间*/
  
if
 
(
copy_from_user(
dev->
mem 
+
 
p,
 
buf,
 
count))
//p是偏移,也就是写入的起始地址
    
ret 
=
  
-
 
EFAULT;
  
else
  
{
    
*
ppos 
+=
 
count;
    
ret 
=
 
count;
    
    
printk(
KERN_INFO 
"written
 
%d
 
bytes(s)
 
from
 
%d/n"
,
 
count,
 
p);
  
}
  
up(&
dev->
sem
);
  
return
 
ret;
}
/*
 
seek文件定位函数
 
*/
static
 
loff_t 
globalmem_llseek(
struct
 
file
 
*
filp
,
 
loff_t 
offset
,
 
int
 
orig
)
{
  
loff_t 
ret
 
=
 
0
;
  
switch
 
(
orig
)
  
{
    
case
 
0
:
   
/*相对文件开始位置偏移*/
      
if
 
(
offset
 
<
 
0
)
      
{
        
ret
 
=
  
-
 
EINVAL;
        
break
;
      
}
      
if
 
((
unsigned
 
int
)
offset
 
>
 
GLOBALMEM_SIZE)
      
{
        
ret
 
=
  
-
 
EINVAL;
        
break
;
      
}
      
filp
->
f_pos 
=
 
(
unsigned
 
int
)
offset
;
      
ret
 
=
 
filp
->
f_pos;
      
break
;
    
case
 
1
:
   
/*相对文件当前位置偏移*/
      
if
 
((
filp
->
f_pos 
+
 
offset
)
 
>
 
GLOBALMEM_SIZE)
      
{
        
ret
 
=
  
-
 
EINVAL;
        
break
;
      
}
      
if
 
((
filp
->
f_pos 
+
 
offset
)
 
<
 
0
)
      
{
        
ret
 
=
  
-
 
EINVAL;
        
break
;
      
}
      
filp
->
f_pos 
+=
 
offset
;
      
ret
 
=
 
filp
->
f_pos;
      
break
;
    
default
:
      
ret
 
=
  
-
 
EINVAL;
      
break
;
  
}
  
return
 
ret
;
}
/*文件操作结构体*/
static
 
const
 
struct
 
file_operations 
globalmem_fops 
=
{
  
.
owner 
=
 
THIS_MODULE,
  
.
llseek 
=
 
globalmem_llseek,
  
.
read 
=
 
globalmem_read,
  
.
write 
=
 
globalmem_write,
  
.
unlocked_ioctl 
=
 
globalmem_unlocked_ioctl,
  
.
open 
=
 
globalmem_open,
  
.
release 
=
 
globalmem_release,
};
/*初始化并注册cdev*/
static
 
void
 
globalmem_setup_cdev(
struct
 
globalmem_dev
 
*
dev
,
 
int
 
index
)
{
  
int
 
err
,
 
devno
 
=
 
MKDEV(
globalmem_major,
 
index
);
  
cdev_init(&
dev
->
cdev
,
 
&
globalmem_fops);
  
dev
->
cdev
.
owner 
=
 
THIS_MODULE;
  
dev
->
cdev
.
ops 
=
 
&
globalmem_fops;
  
err
 
=
 
cdev_add(&
dev
->
cdev
,
 
devno
,
 
1
);
  
if
 
(
err
)
    
printk(
KERN_NOTICE 
"Error
 
%d
 
adding
 
globalmem%d"
,
 
err
,
 
index
);
}
/*设备驱动模块加载函数*/
int
 
globalmem_init(
void
)
{
  
int
 
result
;
  
dev_t 
devno
 
=
 
MKDEV(
globalmem_major,
 
0
);
  
/*
 
申请设备号*/
  
if
 
(
globalmem_major)
    
result
 
=
 
register_chrdev_region(
devno
,
 
1
,
 
"globalmem"
);
  
else
  
/*
 
动态申请设备号
 
*/
  
{
    
result
 
=
 
alloc_chrdev_region(&
devno
,
 
0
,
 
1
,
 
"globalmem"
);
    
globalmem_major 
=
 
MAJOR(
devno
);
  
}
  
  
if
 
(
result
 
<
 
0
)
    
return
 
result
;
 
/*kmalloc*/
 
globalmem_devp 
=
 
kmalloc(
sizeof
(
struct
 
globalmem_dev
),
 
GFP_KERNEL);
  
if
 
(!
globalmem_devp)
    
/*申请失败*/
  
{
    
result
 
=
  
-
 
ENOMEM;
    
goto
 
fail_malloc;
  
}
  
memset(
globalmem_devp,
 
0
,
 
sizeof
(
struct
 
globalmem_dev
));
  
  
globalmem_setup_cdev(
globalmem_devp,
 
0
);
  
sema_init(&
globalmem_devp->
sem
,
1
);
//init_MUTEX(&globalmem_devp->sem);
 
  
/*
 
create
 
your
 
own
 
class
 
under
 
/sysfs
 
*/
  
/*class_create
 
-->
 
class_register()*/
  
my_class 
=
 
class_create(
THIS_MODULE,
 
"globalmem"
);
  
if
(
IS_ERR(
my_class))
 
{
             
printk(
"Err:
 
failed
 
in
 
creating
 
class./n"
);
             
goto
 
fail_malloc;
  
}
 
  
/*
 
register
 
your
 
own
 
device
 
in
 
sysfs,
 
and
 
this
 
will
 
cause
 
udev
 
to
 
create
 
corresponding
 
device
 
node
 
*/
  
device_create(
my_class,
 
NULL,
 
MKDEV(
globalmem_major,
 
0
),
 
NULL,
 
"globalmem"
);
  
printk 
(
KERN_INFO 
"Registered
 
character
 
driver/n"
);
  
return
 
0
;
  
fail_malloc
:
 
unregister_chrdev_region(
devno
,
 
1
);
  
return
 
result
;
}
/*模块卸载函数*/
void
 
globalmem_exit(
void
)
{
  
cdev_del(&
globalmem_devp->
cdev
);
   
/*注销cdev*/
  
kfree(
globalmem_devp);
     
/*释放设备结构体内存*/
  
unregister_chrdev_region(
MKDEV(
globalmem_major,
 
0
),
 
1
);
 
/*释放设备号*/
}
MODULE_AUTHOR(
"Song
 
Baohua"
);
MODULE_LICENSE(
"Dual
 
BSD/GPL"
);
module_param(
globalmem_major,
 
int
,
 
S_IRUGO);
module_init(
globalmem_init);
module_exit(
globalmem_exit);

你可能感兴趣的:(Linux下的简单字符驱动--改自宋宝华《Linux驱动开发详解第2版》在kernel2.6.38通过)