jsp.ajax网站开发典型实例,品牌设计培训,受欢迎的赣州网站建设,古交网站建设1、设备号
主设备号标识设备对应的驱动程序#xff0c;次设备号由内核使用#xff0c;用于确定设备文件所指的设备。
通过次设备号获得一个指向内核设备的直接指针#xff0c;也可将此设备号当作设备本地数组的索引。
设备编号用dev_t表示#xff08;Linux/types.h 32位…1、设备号
主设备号标识设备对应的驱动程序次设备号由内核使用用于确定设备文件所指的设备。
通过次设备号获得一个指向内核设备的直接指针也可将此设备号当作设备本地数组的索引。
设备编号用dev_t表示Linux/types.h 32位其中12位表示主设备号20位表示次设备号。
由dev_t获得主设备号或次设备号MAJOR(dev_t dev); MINOR(dev_t dev)
已知主设备号和次设备号来获取dev_t类型MKDEV(int major, int minor)
获取一个或多个设备编号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);调用成功后dev会保存已分配的第一个编号。
释放设备编号void unregister_chrdev_region(dev_t first, unsigned int count);
接下来驱动程序需要将设备编号和内部函数连接起来。
注下一步可尝试采用动态分配设备号 动态分配设备号缺点不能预先创建设备节点因为分配的设备号不能保证始终一致。
2、文件操作file_operations:
这些操作将与设备编号连接起来。
__user:用于文档表明该指针是一个用户空间地址。
主要成员open, ioctl, read, write, llseek
3、struct file结构 linux/fs.h文件描述符
每打开一个文件内核就会创建一个对应的file结构在open()时创建同时会传递给在该文件上进行操作的所有函数因为file结构中包含file_operations结构而该结构包含了所有驱动操作的函数。
内核中用filp作为执行file结构的指针。
主要成员
Mode_t f_mode; loff_t f_pos; struct file_operations *f_pos; void private_data;
4、inode结构
对单个文件只有一个inode而可能有多个file(由于forkdup操作)。
主要成员
dev_t i_rdev; 对表示设备文件的inode结构该字段包含真正的设备编号。
struct cdev *i_cdev; 该结构表示字符设备的内核的内部结构当inode指向一个字符设备文件时该字段包含指向struct cdev结构的指针。
从inode中获取设备号 iminor(struct inode *inode); imajor(inode);
5、字符设备注册 /linux/cdev.h
内核使用struct cdev结构表示字符设备所以在内核调用该设备操作之前需要分配并注册一个或者多个该结构。
注册有两种方式
新方法
1定义字节的结构
struct my_dev { struct cdev cdev; //此处如果定义的是指针类型则需要申请分配内存
}my_dev;
//my_dev -cdev cdev_alloc(); //如果cdev是指针则需要这一步
my_dev-cdev.ops my_fops;
my_dev-cdev.owner THIS_MODULE;
2再调用cdev_init(struct cdev *cdev, struct file_operations *fops);
3调用cdev_add(struct cdev *dev, dev_t num, unsigned int count);
上面每一步都要判断函数调用是否出错。
旧办法老接口
注册int register_chrdev();移除int unregister_chrdev() 6、各操作函数实现
1open int (*open) (struct inode *inode, struct file *filp)
完成以下工作传入一个inode创建一个file结构
n 检查设备特定错误如未就绪
n 如果设备首次打开则进行初始化
n 必要时更新f_op指针
n 分配并填写filp-private_data
注inode结构是传入的参数对应一个特定的设备这就是为什么要在/dev下mknodde原因而file结构的filp是要修改的参数传出对应该设备的一个文件描述符也就是一个inode可能有多个file描述符而每个描述符需要保存inode的信息即存放在filp-private_data中。
2release int (*release) (struct inode *inode, struct file *filp)
完成工作传入一个inode释放这个file结构
n 释放由open分配的、保存在filp-private_data中的内容
n 在最后一次close时关闭设备
dup 和fork都会在不调用open时创建新的file结构对应同一个inode。
注并不是每个close调用都会调用release只有真正释放设备数据结构的close调用才会调用release。内核对每个file结构维护其被使用次数的计数器无论是fork还是dup都不会创建新的数据结构只会有open创建它们只是增加已有结构中的计数器而已。只有在file结构的计数为0时close才会调用release。
3read ssize_t read(struct file *filp, char __user *buf, count, loff_t *offp)
完成工作传入file将count个字节数据写入用户地址buf修改loff_t
由copy_to_user()实现 返回值说明
n 等于count所请求的字节数读取成功
n 返回值为正但小于count只读取了部分数据
n 为0已经达到文件尾
n 负值出错
4write ssize_t write(struct file *filp, char __user *buf, count, offp);
由copy_from_user()实现
返回值同上。 [cpp] view plaincopy 1驱动代码 Demo.h #ifndef _DEMO_H_ #define _DEMO_H_ #include linux/ioctl.h /*Macros to help debuging*/ #undef PDEBUG #ifdef DEMO_DEBUG #ifdef __KERNEL__ #define PDEBUG(fmt, args...) printk(KERN_DEBUG DEMO: fmt,## args) #else #define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) #endif #else #define PDEBUG(fmt, args...) #endif #define DEMO_MAJOR 224 #define DEMO_MINOR 0 #define COMMAND1 1 #define COMMAND2 2 struct demo_dev { struct cdev cdev; }; ssize_t demo_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos); ssize_t demo_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos); loff_t demo_llseek(struct file *filp, loff_t off, int whence); int demo_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); #endif demo.c #include linux/module.h #include linux/kernel.h #include linux/fs.h #include linux/errno.h #include linux/types.h #include linux/fcntl.h #include linux/cdev.h #include linux/version.h #include linux/vmalloc.h #include linux/ctype.h #include linux/pagemap.h #include demo.h MODULE_AUTHOR(Yangjin); MODULE_LICENSE(Dual BSD/GPL); struct demo_dev *demo_devices; static unsigned char demo_inc 0;//全局变量每次只能打开一个设备 static u8 demo_buffer[256]; int demo_open(struct inode *inode, struct file *filp) { struct demo_dev *dev; if (demo_inc 0) return -ERESTARTSYS; demo_inc; dev container_of(inode-i_cdev, struct demo_dev, cdev); filp-private_data dev; return 0; } int demo_release(struct inode *inode, struct file *filp) { demo_inc--; return 0; } ssize_t demo_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { int result; loff_t pos *f_pos; //pos: offset if (pos 256) { result 0; goto out; } if (count (256 - pos)) count 256 - pos; pos count; if (copy_to_user(buf, demo_buffer*f_pos, count)) { count -EFAULT; goto out; } *f_pos pos; out: return count; } ssize_t demo_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { ssize_t retval -ENOMEM; loff_t pos *f_pos; if (pos 256) goto out; if (count (256 - pos)) count 256 - pos; pos count; if (copy_from_user(demo_buffer*f_pos, buf, count)) { retval -EFAULT; goto out; } *f_pos pos; retval count; out: return retval; } int demo_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { if (cmd COMMAND1) { printk(ioctl command 1 successfully\n); return 0; } if (cmd COMMAND2) { printk(ioctl command 2 successfully\n); return 0; } printk(ioctl error\n); return -EFAULT; } loff_t demo_llseek(struct file *filp, loff_t off, int whence) { loff_t pos; pos filp-f_pos; switch (whence) { case 0: pos off; break; case 1: pos off; break; case 2: default: return -EINVAL; } if ((pos 256) || (pos 0)) return -EINVAL; return filp-f_pos pos; } struct file_operations demo_fops { .owner THIS_MODULE, .llseek demo_llseek, .read demo_read, .write demo_write, .ioctl demo_ioctl, .open demo_open, .release demo_release, }; void demo_cleanup_module(void) { dev_t devno MKDEV(DEMO_MAJOR, DEMO_MINOR); if (demo_devices) { cdev_del(demo_devices-cdev); kfree(demo_devices); } unregister_chrdev_region(devno, 1); } Init module流程 1注册设备号MKDEV 2注册设备驱动程序,即初始化cdev结构嵌入到demo_devices结构中 int demo_init_module(void) { int result; dev_t dev 0; dev MKDEV(DEMO_MAJOR, DEMO_MINOR); result register_chrdev_region(dev, 1, DEMO); if (result 0) { printk(KERN_WARNING DEMO: cant get major %d\n, DEMO_MAJOR); return result; } demo_devices kmalloc(sizeof(struct demo_dev), GFP_KERNEL); if (!demo_devices) { result -ENOMEM; goto fail; } memset(demo_devices, 0, sizeof(struct demo_dev)); cdev_init(demo_devices-cdev, demo_fops); demo_devices-cdev.owner THIS_MODULE; demo_devices-cdev.ops demo_fops; //将创建的字符设备与file_operations中各函数操作连接起来 result cdev_add(demo_devices-cdev, dev, 1); if (result) { printk(KERN_NOTICE error %d adding demo\n, result); goto fail; } return 0; fail: demo_cleanup_module(); return result; } module_init(demo_init_module); module_exit(demo_cleanup_module); 2加载驱动insmod demo.ko再使用lsmod或cat /proc/modules查看驱动是否安装
3创建设备节点mknod /dev/yangjin c 224 0注意此处的节点设备号要与驱动程序中的注册的设备号相同。
4再编写应用程序测试代码
用户测试代码 [cpp] view plaincopy #include sys/types.h #include unistd.h #include fcntl.h #include linux/rtc.h #include linux/ioctl.h #include stdio.h #include stdlib.h #define COMMAND1 1 #define COMMAND2 2 int main() { int fd; int i; char data[256] {0}; int retval; fd open(/dev/yangjin, O_RDWR); if (fd 1) { perror(open error\n); exit(-1); } printf(open /dev/yangjin successfully\n); retval ioctl(fd, COMMAND1, 0); if (retval -1) { perror(ioctl error\n); exit(-1); } printf(ioctl command 1 successfully\n); retval write(fd, yangjin, 7); if (retval -1) { perror(write error\n); exit(-1); } retval lseek(fd, 0, 0); if (retval -1) { perror(lseek error\n); exit(-1); } retval read(fd, data, 10); if (retval -1) { perror(read error\n); exit(-1); } printf(read successfully: %s\n, data); close(fd); return 0; }