字符设备(顺序读写,不带缓冲)
块设备(读写顺序不固定,带读写缓冲)常用sync命令(强行写入硬件) 同步数据使数据真正写进磁盘不然可能还在缓冲区
网络设备:这家伙破坏了linux一切皆文件规则。
字符设备框架:
硬件上有个字符设备,内核中就有个cdev结构与之对应
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;//设备号
unsigned int count;
};
每个cdev都有个设备号
设备号(32bits)=主设备号(高12bits)+次设备号(20bits)
主设备号:代表一个类型的设备
次设备号:用于区分设备的不同个体
主设备号的选取
->静态分配
看现在内核中那些主设备号没有被使用,选择一个
查看 cat/proc/devoces已用的设备号
看内核源码目录下即Documentation\device.txt看看使用那些设备号
register_chedev_region(dev_t from,unsigned count,const char name)可以连续注册多个字符设备号
from要注册的起始设备号
count 连续注册的设备个数
name名字
dev_t __u32 -> unsigned int 16位和32位移植兼容性才这么设计的
#definrMINORBITS 20
MKDEV(ma(主设备号),mi(次设备号)) 他是个宏 其实源码也就做了ma<<MINORBITS|mi)
unregister_chrdev_region(dev,nsigned count)卸载字符设备
->动态分配 但不是随机分配
alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char name)
注释:
/**
* alloc_chrdev_region() - register a range of char device numbers
* @dev: output parameter for first assigned number
* @baseminor: first of the requested range of minor numbers
* @count: the number of minor numbers required
* @name: the name of the associated device or driver
*
* Allocates a range of char device numbers. The major number will be
* chosen dynamically, and returned (along with the first minor number)
* in @dev. Returns zero or a negative error code.
*/
获取主设备号:
MAJOR(dev) dev>>MINORBITS
cdev的操作函数
cdev_init()//初始化cdev
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
memset(cdev, 0, sizeof *cdev);
INIT_LIST_HEAD(&cdev->list);
kobject_init(&cdev->kobj, &ktype_cdev_default);
cdev->ops = fops;
}
cdev_add()//向内核注册cdev
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
p->dev = dev;
p->count = count;
return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
.....
};
_exit 要逆序消除 _init产生的影响
cdev_del()
unregister_chrdev_region(dev,nsigned count)
手工创建设备节点文件
mknod /dev/xxx c 248 0
主 次
arm-none-linux-gnueabi-gcc -o
/* remove by cym 20130408 support for MT660.ko */
#if 0
//#ifdef CONFIG_SMM6260_MODEM
#if 1// liang, Pixtree also need to use ioctl interface...
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
#endif
#endif
/* end remove */
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
/* add by cym 20130408 support for MT6260 and Pixtree */
#if defined(CONFIG_SMM6260_MODEM) || defined(CONFIG_USE_GPIO_AS_I2C)
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
#endif
/* end add */
简单纯字符小示例:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
MODULE_LICENSE("Dua BSD/GPL");
MODULE_AUTHOR("Songmao");
MODULE_DESCRIPTION("cdev tast");
#define TCDEV_MAJOR 0
#define TCDEV_MINOR 0
#define TCDEV_NUM 1
#define TCDEV_NAME "mytcdev"
int tcdev_major =TCDEV_MAJOR,tcdev_minor=TCDEV_MINOR;
module_param(tcdev_major,int,S_IRWXU);
module_param(tcdev_minor,int,S_IRWXU);
static int tcdev_open(struct inode * node,struct file * filep)
{
printk(KERN_INFO "tcdev open\n");
return 0;
}
static ssize_t tcdev_read(struct file *file, char __user *buf, size_t count, loff_t *fops)
{
printk(KERN_INFO "tcdev read\n");
return 0;
}
static ssize_t tcdev_write (struct file *file, char __user *buf, size_t count, loff_t *fops)
{
printk(KERN_INFO "tcdev write\n");
return 0;
}
static int tcdev_release(struct inode * node,struct file * filep)
{
printk(KERN_INFO "tcdev write\n");
return 0;
}
static struct file_operations tcdev_fops = {
.owner=THIS_MODULE,
.open=tcdev_open,
.read=tcdev_read,
.write=tcdev_write,
.release=tcdev_release,
};
static struct cdev tcdev;
static int __init tcdev_init(void)
{
int ret =-EFAULT;
dev_t tcdev_t;
tcdev_t=MKDEV(tcdev_major,tcdev_minor);
printk(KERN_INFO "tcdev start ...\n");
if(TCDEV_MAJOR>0){
ret= alloc_chrdev_region(&tcdev_t,tcdev_minor,TCDEV_NUM,TCDEV_NAME);
if(ret<0){
printk(KERN_ERR"tcdev cdev num apply is fail\n");
goto tcdev_cdev_fail;
}
}
else{
ret=register_chrdev_region(tcdev_t,TCDEV_NUM,TCDEV_NAME);
if(ret<0){
printk(KERN_ERR "tcdev cdev num apply is fail\n");
goto tcdev_cdev_fail;
}
}
printk(KERN_INFO "tcdev cdev num apply is success,dev_t is %d\n",tcdev_t);
memset(&tcdev,0,sizeof(struct cdev));
cdev_init(&tcdev,&tcdev_fops);
ret=cdev_add(&tcdev,tcdev_t,TCDEV_NUM);
if(ret<0){
printk(KERN_ERR"tcdev cdev cdev_add is fail\n");
goto tcdev_cdev_add_fail;
}
printk(KERN_INFO"tcdev cdev_add apply is success\n");
tcdev_cdev_add_fail:
unregister_chrdev_region(&tcdev,TCDEV_NUM);
tcdev_cdev_fail:
return 0;
}
static void __exit tcdev_exit (void)
{
printk(KERN_INFO "tcdev stop ...\n");
cdev_del(&tcdev);
printk(KERN_INFO"tcdev del success\n");
unregister_chrdev_region(&tcdev,TCDEV_NUM);
printk(KERN_INFO "tcdev dev_num del success\n");
return 0;
}
module_init(tcdev_init);
module_exit(tcdev_exit);