基础知识
主设备号和次设备号
#include <linux/types.h>
dev_t
MAJOR ( dev_t dev) ;
MINOR ( dev_t dev) ;
MKDEV ( int major, int minor) ;
#include <linux/fs.h>
int register_chrdev_region ( dev_t first, unsigned int count, char * name) ;
int alloc_chrdev_region ( dev_t * dev, unsigned int firstminor, unsigned int count, char * name) ;
void unregitster_chrdev_region ( dev_t first, unsigned int count) ;
if ( scull_major) {
dev = MKDEV ( scull_major, scull_minor) ;
result = register_chrdev_region ( dev, scull_nr_devs, "scull" ) ;
} else {
result = alloc_chrdev_region ( & dev, scull_minor, scull_nr_devs, "scull" ) ;
scull_major = MAJOR ( dev) ;
}
if ( result < 0 ) {
printk ( KERN_WARNING "scull: can't get major %d\n" , scull_major) ;
return result;
}
重要数据结构
#include <linux/fs.h>
inode
dev_t i_rdev;
struct cdev * i_cdev;
- 内核用inode结构在内部表示文件;
- 和file结构不同,file结构表示打开的文件描述符;
- 对单个文件,可能会有许多个表示打开的文件描述符的file结构,但他们都指向单个inode结构
unsigned int iminor ( struct inode * inode) ;
unsigned int imajor ( struct inode * inode) ;
fops → struct file_operations
sturct module * owner;
int ( * open) ( struct inode * , struct file * ) ;
- 检查设备特定错误;
- 初始化首次打开的设备;
- 如有必要,更新`f_op`指针;
- 分配并填写置于`filp-> private_data`里的数据结构;
#include <linux/kernel.h>
container_of ( pointer, container_type, container_field) ;
int ( * release) ( struct inode * , struct file * ) ;
ssize_t ( * read) ( struct file * , char __user * , size_t, loff_t * ) ;
ssize_t ( * write) ( struct file * , char __user * , size_t, loff_t * ) ;
#include <asm/uaccess.h>
#include <linux/uaccess.h>
unsigned long copy_to_user ( void __user * to, const void * from, unsigned long count) ;
unsigned long copy_from_user ( void * to, const void __user * from, unsigned long count) ;
filp → struct file
mode_t f_mode;
lofft_t f_pos;
unsigned int f_flags;
struct file_operations * f_op;
void * private_data;
struct dentry * f_dentry;
字符设备的注册及移除
方式一
#include <linux/cdev.h>
void cdev_init ( struct cdev * cdev, struct file_operation * fops) ;
int cdev_add ( struct cdev * dev, dev_t num, unsigned int count) ;
void cdev_del ( struct cdev * dev) ;
方式二
int register_chrdev ( unsigned int major, const char * name, struct file_operations * fops) ;
int unregister_chrdev ( unsigned int major, const char * name) ;
字符设备驱动编写要点
获取设备编号;
if ( scull_major) {
dev = MKDEV ( scull_major, scull_minor) ;
result = register_chrdev_region ( dev, scull_nr_devs, "scull" ) ;
} else {
result = alloc_chrdev_region ( & dev, scull_minor, scull_nr_devs,
"scull" ) ;
scull_major = MAJOR ( dev) ;
}
if ( result < 0 ) {
printk ( KERN_WARNING "scull: can't get major %d\n" , scull_major) ;
return result;
}
注册字符设备;
static void scull_setup_cdev ( struct scull_dev * dev, int index)
{
int err, devno = MKDEV ( scull_major, scull_minor + index) ;
cdev_init ( & dev-> cdev, & scull_fops) ;
dev-> cdev. owner = THIS_MODULE;
dev-> cdev. ops = & scull_fops;
err = cdev_add ( & dev-> cdev, devno, 1 ) ;
if ( err)
printk ( KERN_NOTICE "Error %d adding scull%d" , err, index) ;
}
example
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
int example_major = 0 ;
int example_minor = 0 ;
static unsigned int example_nr_devs = 1 ;
typedef struct EXAMPLE_CDEV
{
struct cdev cdev;
} EXAMPLE_CDEV_STRU;
static EXAMPLE_CDEV_STRU example_cdev;
module_param ( example_major, int , S_IRUGO) ;
module_param ( example_minor, int , S_IRUGO) ;
MODULE_LICENSE ( "Dual BSD/GPL" ) ;
static int example_open ( struct inode * inode, struct file * filp)
{
printk ( "example: open\n" ) ;
return 0 ;
}
static int example_close ( struct inode * inode, struct file * filp)
{
printk ( "example: close\n" ) ;
return 0 ;
}
static ssize_t example_read ( struct file * filp, char __user * buf, size_t size, loff_t * f_pos)
{
size_t count;
uint8_t byte;
char data[ ] = "0123456789\r\n" ;
printk ( "example: read (size=%zu)\n" , size) ;
for ( count = 0 ; ( count < size) && ( * f_pos) < strlen ( data) ; ++ ( * f_pos) , ++ count) {
byte = data[ * f_pos] ;
if ( copy_to_user ( buf + count, & byte, 1 ) != 0 ) {
break ;
}
printk ( "example: read (buf[%zu]=%c)\n" , count, ( unsigned ) byte) ;
}
return count;
}
static ssize_t example_write ( struct file * filp, const char __user * buf, size_t size, loff_t * f_pos)
{
size_t count;
uint8_t byte;
printk ( "example: write (size=%zu)\n" , size) ;
for ( count = 0 ; count < size; ++ count) {
if ( copy_from_user ( & byte, buf + count, 1 ) != 0 ) {
break ;
}
printk ( "example: write (buf[%zu]=%c)\n" , count, ( unsigned ) byte) ;
}
return count;
}
static struct file_operations example_fops = {
. open = example_open,
. release = example_close,
. read = example_read,
. write = example_write,
} ;
static void example_setup_cdev ( EXAMPLE_CDEV_STRU * dev, int index)
{
int err, devno = MKDEV ( example_major, example_minor + index) ;
cdev_init ( & dev-> cdev, & example_fops) ;
dev-> cdev. owner = THIS_MODULE;
dev-> cdev. ops = & example_fops;
err = cdev_add ( & dev-> cdev, devno, 1 ) ;
if ( err)
printk ( KERN_NOTICE "Error %d adding example-%d" , err, index) ;
}
static int example_init ( void )
{
dev_t dev;
int result;
printk ( "example: init\n" ) ;
if ( example_major) {
dev = MKDEV ( example_major, example_minor) ;
result = register_chrdev_region ( dev, example_nr_devs, "example" ) ;
} else {
result = alloc_chrdev_region ( & dev, example_minor, example_nr_devs,
"example" ) ;
example_major = MAJOR ( dev) ;
}
if ( result < 0 ) {
printk ( KERN_WARNING "example: can't get major %d\n" , example_major) ;
return result;
}
example_setup_cdev ( & example_cdev, 0 ) ;
printk ( "example: %s driver(major %d) installed.\n" , "example" , example_major) ;
return 0 ;
}
static void example_exit ( void )
{
dev_t dev = MKDEV ( example_major, example_minor) ;
printk ( "example: exit\n" ) ;
cdev_del ( & example_cdev. cdev) ;
unregister_chrdev_region ( dev, example_nr_devs) ;
printk ( "example: %s driver removed.\n" , "example" ) ;
}
module_init ( example_init) ;
module_exit ( example_exit) ;
#!/bin/sh
module= "example"
echo "-------begin--------"
insmod ./$module .ko
dmesg | tail -2
major= $( awk "\$2 ==\"$module \" {print \$1 }" /proc/devices)
sudo rm -f /dev/$module
sudo mknod /dev/$module c $major 0
echo -n "hello world" > /dev/$module
dmesg | tail -n 14
cat /dev/$module
dmesg | tail -n 17
echo "-------end--------"
其他
tail
tail [ -f ] [ -c Number | -n Number | -m Number | -b Number | -k Number ] [ File ]
参数解释:
-f 该参数用于监视File文件增长。
-c Number 从 Number 字节位置读取指定文件
-n Number 从 Number 行位置读取指定文件。
-m Number 从 Number 多字节字符位置读取指定文件,比方你的文件假设包括中文字,假设指定-c参数,可能导致截断,但使用-m则会避免该问题。
-b Number 从 Number 表示的512字节块位置读取指定文件。
-k Number 从 Number 表示的1KB块位置读取指定文件。
File 指定操作的目标文件名称
上述命令中,都涉及到number,假设不指定,默认显示10行。Number前面可使用正负号,表示该偏移从顶部还是从尾部開始计算。
tail可运行文件一般在/usr/bin/以下。
dmesg
echo
mknode
mknod [ options] name { bc} major minor
mknod [ options] name p
awk
awk 'Pattern {Action}' filename
-其中Pattern用来指定判断条件
-{ } 中包含的是awk的动作,也就是awk对记录的操作
major= $( awk "\$2 ==\"example\" {print \$1 }" /proc/devices)