文章目录
1.自动创建设备节点
1.1自动创建设备节点的历史演变
(1)mknod (rcS)
(2)devfs (2.4内核)
(3)udev/mdev (2.6内核版本之后)
1.2udev/mdev的原理
mdev就是一个轻量级的udev (和udev的原理一样),常用在嵌入式设备上。
1.3内核中提交信息的函数API
struct class * class_create(owner, name)
功能:提交目录信息
参数:
@owner:THIS_MODULE (以后遇到owner就填写THIS_MODULE即可,它是和入口出口相关的宏)
@name:目录名
返回值:成功返回结构体指针,失败返回错误码指针 (void *)-12;
//IS_ERR:判断是否是错误,判断这个地址是否大于 > (unsigned long)(-4095)
//PTR_ERR:错误码指针还原为错误码
//ERR_PTR:错误码转为错误码指针
struct class *cls;
cls = class_create(THIS_MODULE,"hello");
if(IS_ERR(cls)){
printk("class create error\n");
return PTR_ERR(cls);
}
void class_destroy(struct class *cls)
功能:销毁目录
参数:
@cls:class的指针
返回值:无
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
功能:向上提交设备节点的信息
参数:
@class:class结构体指针
@parent:NULL
@devt:设备号
MKDEV(ma,mi) :根据主次设备号合成设备号
MAJOR(dev) :得到主设备号(dev设备号)
MINOR(dev):得到次设备号(dev设备号)
@drvdata:NULL
@fmt:节点名 "mycdev%d",i
返回值:成功返回结构体指针,失败返回错误码指针
void device_destroy(struct class *class, dev_t devt)
功能:销毁设备信息
参数:
@class:class结构体指针
@devt:设备号
返回值:无
1.4自动创建设备节点的实例
myled.h
#ifndef __MYLED_H__
#define __MYLED_H__
#define GPIOE_PHY_BASE 0x50006000 //(moder odr)
#define RCC_ENB 0x50000a28 //rcc_enb
typedef struct {
volatile unsigned int MODER; // 0x00
volatile unsigned int OTYPER; // 0x04
volatile unsigned int OSPEEDR; // 0x08
volatile unsigned int PUPDR; // 0x0C
volatile unsigned int IDR; // 0x10
volatile unsigned int ODR; // 0x14
volatile unsigned int BSRR; // 0x18
volatile unsigned int LCKR; // 0x1C
volatile unsigned int AFRL; // 0x20
volatile unsigned int AFRH; // 0x24
volatile unsigned int BRR; // 0x28
volatile unsigned int res;
volatile unsigned int SECCFGR; // 0x30
}gpio_t;
#endif
myled.c
#include "myled.h"
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#define CNAME "myled"
int major = 0;
char kbuf[128] = { 0 };
gpio_t* gpioe = NULL;
unsigned int* rcc;
struct class* cls;
struct device* dev;
#define LED1_ON (gpioe->ODR |= (1 << 10)) // led1 on
#define LED1_OFF (gpioe->ODR &= ~(1 << 10)) // led1 off
int myled_open(struct inode* inode, struct file* filp)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
// 1.映射地址
gpioe = ioremap(GPIOE_PHY_BASE, sizeof(gpio_t));
if (gpioe == NULL) {
printk("ioremap gpioe base error\n");
return -ENOMEM;
}
rcc = ioremap(RCC_ENB, 4);
if (rcc == NULL) {
printk("ioremap rcc error\n");
return -ENOMEM;
}
// 2.初始化灯
*rcc |= (1 << 4); // rcc enable gpioe
gpioe->MODER &= ~(3 << 20); // clear bit 20-21
gpioe->MODER |= (1 << 20); // output
gpioe->ODR &= ~(1 << 10); // led1 off
return 0;
}
ssize_t myled_read(struct file* filp,
char __user* ubuf, size_t size, loff_t* offs)
{
int ret;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (size > sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_to_user(ubuf, kbuf, size);
if (ret) {
printk("copy data to user error\n");
return -EINVAL;
}
return size;
}
ssize_t myled_write(struct file* filp,
const char __user* ubuf, size_t size, loff_t* offs)
{
int ret;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (size > sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_from_user(kbuf, ubuf, size);
if (ret) {
printk("copy data from user error\n");
return -EINVAL;
}
kbuf[0] == 1 ? LED1_ON : LED1_OFF;
return size;
}
int myled_close(struct inode* inode, struct file* filp)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
gpioe->ODR &= ~(1 << 10); // led1 off
iounmap(gpioe);
iounmap(rcc);
return 0;
}
const struct file_operations fops = {
.open = myled_open,
.read = myled_read,
.write = myled_write,
.release = myled_close,
};
static int __init myled_init(void)
{
int i;
//创建字符设备驱动
major = register_chrdev(0, CNAME, &fops);
if (major < 0) {
printk("register char device driver error\n");
return major;
}
//自动创建设备节点
cls = class_create(THIS_MODULE, "hello");
if (IS_ERR(cls)) {
printk("class create error\n");
return PTR_ERR(cls);
}
for (i = 0; i < 3; i++) {
dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);
if (IS_ERR(dev)) {
printk("device create error\n");
return PTR_ERR(dev);
}
}
return 0;
}
static void __exit myled_exit(void)
{
int i;
for (i = 0; i < 3; i++) {
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
//销毁字符设备驱动
unregister_chrdev(major, CNAME);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
test.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
char buf[128] = {0};
int main(int argc, const char *argv[])
{
int fd;
if((fd = open("/dev/myled0",O_RDWR)) < 0){
perror("open error");
exit(EXIT_FAILURE);
}
while(1){
buf[0] = !buf[0];
write(fd,buf,sizeof(buf));
sleep(1);
}
close(fd);
return 0;
}
2.ioctl函数的使用
2.1为什么需要ioctl
ioctl:设备的控制,linux内核想把数据的读写和设备的控制拆分开来。
以后设备的控制就通过ioctl完成,数据的读写通过read/write完成。
例如:可以通过ioctl设置摄像头的工作模式(拍摄图片的大小,拍摄图片的格式),
当拍摄完照片之后,通过read函数将图片读取到用户空间。
2.2ioctl的API接口
us:
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);
功能:设备的控制
参数:
@fd:文件描述符
@request:请求码
@... :可以不填写,如果填写填写地址
返回值:成功返回0,失败返回-1置位错误码
-------------------------------------------------------
ks:
fops:long (*unlocked_ioctl) (struct file *, unsigned int cmd, unsigned long args)
{
switch(cmd){
case LED1_ON: //将LED1灯点亮
break;
case LED1_OFF: //将LED1灯熄灭
break;
}
}
2.3请求码的定义规则
2.4ioctl实例
cmd.h
#ifndef __CMD_H__
#define __CMD_H__
typedef struct{
int width;
int high;
}image_t;
#define LED1_ON _IO('g',0)
#define LED1_OFF _IO('g',1)
#define UACCESS_STRUCT _IOW('g',2,image_t)
#define GET_CMD_SIZE(cmd) ((cmd>>16)&0x3fff)
#endif
myled.c
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include "cmd.h"
#define CNAME "myled"
int major = 0;
char kbuf[128] = { 0 };
unsigned int* rcc;
struct class* cls;
struct device* dev;
int myled_open(struct inode* inode, struct file* filp)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
ssize_t myled_read(struct file* filp,
char __user* ubuf, size_t size, loff_t* offs)
{
int ret;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (size > sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_to_user(ubuf, kbuf, size);
if (ret) {
printk("copy data to user error\n");
return -EINVAL;
}
return size;
}
ssize_t myled_write(struct file* filp,
const char __user* ubuf, size_t size, loff_t* offs)
{
int ret;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (size > sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_from_user(kbuf, ubuf, size);
if (ret) {
printk("copy data from user error\n");
return -EINVAL;
}
return size;
}
long myled_ioctl (struct file *file,
unsigned int cmd, unsigned long args)
{
int ret;
image_t img;
switch(cmd){
case LED1_ON:
printk("LED1 ON\n");
break;
case LED1_OFF:
printk("LED1 OFF\n");
break;
case UACCESS_STRUCT:
ret = copy_from_user(&img,(void *)args,
GET_CMD_SIZE(UACCESS_STRUCT));
if(ret){
printk("copy data from user error\n");
return -EINVAL;
}
printk("width = %d,high = %d\n",img.width,img.high);
break;
}
return 0;
}
int myled_close(struct inode* inode, struct file* filp)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
const struct file_operations fops = {
.open = myled_open,
.read = myled_read,
.write = myled_write,
.unlocked_ioctl = myled_ioctl,
.release = myled_close,
};
static int __init myled_init(void)
{
int i;
//创建字符设备驱动
major = register_chrdev(0, CNAME, &fops);
if (major < 0) {
printk("register char device driver error\n");
return major;
}
//自动创建设备节点
cls = class_create(THIS_MODULE, "hello");
if (IS_ERR(cls)) {
printk("class create error\n");
return PTR_ERR(cls);
}
for (i = 0; i < 3; i++) {
dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);
if (IS_ERR(dev)) {
printk("device create error\n");
return PTR_ERR(dev);
}
}
return 0;
}
static void __exit myled_exit(void)
{
int i;
for (i = 0; i < 3; i++) {
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
//销毁字符设备驱动
unregister_chrdev(major, CNAME);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
test.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include "cmd.h"
int main(int argc, const char *argv[])
{
int fd;
if((fd = open("/dev/myled0",O_RDWR)) < 0){
perror("open error");
exit(EXIT_FAILURE);
}
ioctl(fd,LED1_ON);
ioctl(fd,LED1_OFF);
image_t img = {
1024,600
};
ioctl(fd,UACCESS_STRUCT,&img);
close(fd);
return 0;
}
2.5练习
1.通过ioctl实现流水灯的效果
2.通过ioctl用内核空间向用户空间传递char [128];
cmd.h
#ifndef __CMD_H__
#define __CMD_H__
typedef struct{
int width;
int high;
}image_t;
#define LED1_ON _IO('g',0)
#define LED1_OFF _IO('g',1)
#define UACCESS_STRUCT _IOW('g',2,image_t)
#define UACCESS_BUFFER _IOR('g',3,char [128])
#define GET_CMD_SIZE(cmd) ((cmd>>16)&0x3fff)
#endif
myled.h
#ifndef __MYLED_H__
#define __MYLED_H__
#define GPIOE_PHY_BASE 0x50006000 //(moder odr)
#define RCC_ENB 0x50000a28 //rcc_enb
typedef struct {
volatile unsigned int MODER; // 0x00
volatile unsigned int OTYPER; // 0x04
volatile unsigned int OSPEEDR; // 0x08
volatile unsigned int PUPDR; // 0x0C
volatile unsigned int IDR; // 0x10
volatile unsigned int ODR; // 0x14
volatile unsigned int BSRR; // 0x18
volatile unsigned int LCKR; // 0x1C
volatile unsigned int AFRL; // 0x20
volatile unsigned int AFRH; // 0x24
volatile unsigned int BRR; // 0x28
volatile unsigned int res;
volatile unsigned int SECCFGR; // 0x30
}gpio_t;
#endif
myled.c
#include "myled.h"
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include "cmd.h"
#define CNAME "myled"
int major = 0;
char kbuf[128] = { 0 };
gpio_t* gpioe = NULL;
unsigned int* rcc;
struct class* cls;
struct device* dev;
#define SET_LED1_ON (gpioe->ODR |= (1 << 10)) // led1 on
#define SET_LED1_OFF (gpioe->ODR &= ~(1 << 10)) // led1 off
int myled_open(struct inode* inode, struct file* filp)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
// 1.映射地址
gpioe = ioremap(GPIOE_PHY_BASE, sizeof(gpio_t));
if (gpioe == NULL) {
printk("ioremap gpioe base error\n");
return -ENOMEM;
}
rcc = ioremap(RCC_ENB, 4);
if (rcc == NULL) {
printk("ioremap rcc error\n");
return -ENOMEM;
}
// 2.初始化灯
*rcc |= (1 << 4); // rcc enable gpioe
gpioe->MODER &= ~(3 << 20); // clear bit 20-21
gpioe->MODER |= (1 << 20); // output
gpioe->ODR &= ~(1 << 10); // led1 off
return 0;
}
ssize_t myled_read(struct file* filp,
char __user* ubuf, size_t size, loff_t* offs)
{
int ret;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (size > sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_to_user(ubuf, kbuf, size);
if (ret) {
printk("copy data to user error\n");
return -EINVAL;
}
return size;
}
ssize_t myled_write(struct file* filp,
const char __user* ubuf, size_t size, loff_t* offs)
{
int ret;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (size > sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_from_user(kbuf, ubuf, size);
if (ret) {
printk("copy data from user error\n");
return -EINVAL;
}
return size;
}
long myled_ioctl (struct file *file,
unsigned int cmd, unsigned long args)
{
int ret;
switch(cmd){
case LED1_ON:
SET_LED1_ON;
break;
case LED1_OFF:
SET_LED1_OFF;
break;
case UACCESS_BUFFER:
memcpy(kbuf,"this is test ioctl access buffer to user",41);
ret = copy_to_user((void *)args,kbuf,
GET_CMD_SIZE(UACCESS_BUFFER));
if(ret){
printk("access data to user error\n");
return -EINVAL;
}
break;
}
return 0;
}
int myled_close(struct inode* inode, struct file* filp)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
gpioe->ODR &= ~(1 << 10); // led1 off
iounmap(gpioe);
iounmap(rcc);
return 0;
}
const struct file_operations fops = {
.open = myled_open,
.read = myled_read,
.write = myled_write,
.unlocked_ioctl = myled_ioctl,
.release = myled_close,
};
static int __init myled_init(void)
{
int i;
//创建字符设备驱动
major = register_chrdev(0, CNAME, &fops);
if (major < 0) {
printk("register char device driver error\n");
return major;
}
//自动创建设备节点
cls = class_create(THIS_MODULE, "hello");
if (IS_ERR(cls)) {
printk("class create error\n");
return PTR_ERR(cls);
}
for (i = 0; i < 3; i++) {
dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);
if (IS_ERR(dev)) {
printk("device create error\n");
return PTR_ERR(dev);
}
}
return 0;
}
static void __exit myled_exit(void)
{
int i;
for (i = 0; i < 3; i++) {
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
//销毁字符设备驱动
unregister_chrdev(major, CNAME);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
test.c
#include "cmd.h"
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
char buf[128] = { 0 };
int main(int argc, const char* argv[])
{
int fd;
if ((fd = open("/dev/myled0", O_RDWR)) < 0) {
perror("open error");
exit(EXIT_FAILURE);
}
ioctl(fd, UACCESS_BUFFER, buf);
printf("buf = %s\n", buf);
//alt+shift+f
while (1) {
ioctl(fd, LED1_ON);
sleep(1);
ioctl(fd, LED1_OFF);
sleep(1);
}
close(fd);
return 0;
}
3.字符分步实现流程
3.1字符设备驱动的框架结构
3.2分步实现字符设备驱动的API
1.分配对象
struct cdev {
struct module *owner; //THIS_MODULE
const struct file_operations *ops; //操作方法结构体
struct list_head list; //构成内存链表
dev_t dev; //设备号
unsigned int count; //设备的个数
} ;
struct cdev cdev;
struct cdev *cdev = cdev_alloc(); //申请内存
kfree(cdev); //释放内存
2.对象的初始化
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
功能:初始化字符设备驱动
参数:
@cdev:cdev的结构体指针
@fops:操作方法结构体指针
返回值:无
int register_chrdev_region(dev_t from, unsigned count, const char *name)
功能:静态指定设备号
参数:
@from:设备号起始值
@count:设备的个数
@name:设备的名字 cat /proc/devices
返回值:成功返回0,失败返回错误码
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
const char *name)
功能:动态申请设备号
参数:
@dev:申请到的设备号
@basminor:次设备号的起始值
@count:设备的个数
@name:设备名 cat /proc/devices
返回值:成功返回0,失败返回错误码
void unregister_chrdev_region(dev_t from, unsigned count)
功能:注销设备号
参数;
@from:设备号的起始值
@count:设备的个数
返回值:无
3.字符设备驱动的注册
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
功能:注册字符设备驱动
参数:
@p:cdev的结构体指针
@dev:设备号
@count:设备的个数
返回值:成功返回0,失败返回错误码
4.注销
void cdev_del(struct cdev *p)
功能:注销字符设备驱动
参数:
@p:cdev的结构体指针
返回值:无
3.3字符设备驱动分步实现实例
mycdev.c
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#define CNAME "mycdev"
#define COUNT 3
struct cdev* cdev;
int major =238;
int minor = 0;
char kbuf[128] = { 0 };
struct class *cls;
struct device *dev;
int mycdev_open(struct inode* inode, struct file* filp)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
ssize_t mycdev_read(struct file* filp,
char __user* ubuf, size_t size, loff_t* offs)
{
int ret;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (size > sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_to_user(ubuf, kbuf, size);
if (ret) {
printk("copy data to user error\n");
return -EINVAL;
}
return size;
}
ssize_t mycdev_write(struct file* filp,
const char __user* ubuf, size_t size, loff_t* offs)
{
int ret;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (size > sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_from_user(kbuf, ubuf, size);
if (ret) {
printk("copy data from user error\n");
return -EINVAL;
}
return size;
}
int mycdev_close(struct inode* inode, struct file* filp)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
const struct file_operations fops = {
.open = mycdev_open,
.read = mycdev_read,
.write = mycdev_write,
.release = mycdev_close,
};
static int __init mycdev_init(void)
{
int ret,i;
dev_t devno;
// 1.分配对象
cdev = cdev_alloc();
if (cdev == NULL) {
printk("alloc cdev memory error\n");
ret = -ENOMEM;
goto ERR1;
}
// 2.对象初始化
cdev_init(cdev, &fops);
// 3.设备号的申请
if (major == 0) {
//动态申请
ret = alloc_chrdev_region(&devno, minor, COUNT, CNAME);
if (ret) {
printk("dynamic:request device number error\n");
goto ERR2;
}
major = MAJOR(devno);
minor = MINOR(devno);
} else {
//静态指定
ret = register_chrdev_region(MKDEV(major, minor), COUNT, CNAME);
if (ret) {
printk("static:request device number error\n");
goto ERR2;
}
}
// 4.字符设备驱动的注册
ret = cdev_add(cdev,MKDEV(major,minor),COUNT);
if(ret){
printk("register chardev error\n");
goto ERR3;
}
//5.自动创建设备节点
cls = class_create(THIS_MODULE, CNAME);
if (IS_ERR(cls)) {
printk("class create error\n");
ret = PTR_ERR(cls);
goto ERR4;
}
for (i = minor; i < minor+3; i++) {
dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mycdev%d", i);
if (IS_ERR(dev)) {
printk("device create error\n");
ret = PTR_ERR(dev);
goto ERR5;
}
}
return 0; /*!!!!!!!!!!!!!!不要忘记!!!!!!!!!!!!!!!!!!!!!!!*/
ERR5:
for(--i;i>=minor;i--){
device_destroy(cls,MKDEV(major,i));
}
class_destroy(cls);
ERR4:
cdev_del(cdev);
ERR3:
unregister_chrdev_region(MKDEV(major,minor),COUNT);
ERR2:
kfree(cdev);
ERR1:
return ret;
}
static void __exit mycdev_exit(void)
{
int i;
//注销设备节点
for (i = minor; i < minor+3; i++) {
device_destroy(cls,MKDEV(major,i));
}
class_destroy(cls);
//字符设备驱动的注销
cdev_del(cdev);
//设备号释放
unregister_chrdev_region(MKDEV(major,minor),COUNT);
//释放cdev的内存空间
kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
4.file结构体的功能
4.1file的引入
open(“/dev/myled0”)—>inode号—>inode结构体—>cdev---->fops—>驱动的open函数
read(fd,buf,sizeof(buf))
write(fd,buf,sizeof(buf))
close(fd)
read、write、close不不是通过文件找到的驱动,而是通过文件描述符fd找到的驱动的read,write,close
总结:fd是和file有关系
2.2fd和file结构体的关系
fd是在进程中调用open函数的时候产生的,当进程结束的时候,文件描述符就会被销毁了。
所以fd一定是保存在进程的结构体中的。进程的结构体是task_struct
struct task_struct {
volatile long state;
//进程的状态 运行态,可中断的等待态,不可中断的等待态,停止态,僵尸态,死亡态
int on_cpu;
//标识进程在那个处理器上运行
int prio;
//进程的优先级
pid_t pid;
//进程的标识符
pid_t tgid;
//组id
struct task_struct __rcu *real_parent;
//真实的父进程
struct task_struct __rcu *parent;
//父进程
struct list_head children;
//子进程
struct list_head sibling;
//兄弟进程
struct task_struct *group_leader;
//组长进程
struct files_struct *files;
//这个结构体就是记录的打开文件的所有的信息
};
struct files_struct {
struct file * fd_array[NR_OPEN_DEFAULT];
};
fd--->fd_array[fd]--->file--->f_op--->驱动的open read write close
只要文件被打开一次就会产生一个file结构体,
file结构体是用来记录打开文件的信息的
struct file {
struct path f_path; //路径的结构体
struct inode *f_inode; //文件的结构体
const struct file_operations *f_op; //操作方法结构体
unsigned int f_flags; //打开文件的权限,文件是阻塞还是非阻塞打开
fmode_t f_mode; //文件的权限
loff_t f_pos; //文件光标的位置
void *private_data; //私有数据,可以用作函数间传参
};
4.3总结file
在使用open打开一个文件的时候,其实是在内核中创建了一个file的结构体,这个
结构体记录的打开文件的所有的信息,将这个file结构体放入到fd_arrary数组中,
数据的下标就是文件描述符,将下标返回给用户。用户通过fd找到驱动的操作方法
结构体中驱动函数的流程如下:
fd—>fd_array[fd]—>file—>f_op—>驱动的open read write close
5.设备号的查找方式(哈希)
设备号在内核中通过哈希表来存储。设备号在查找的时候通过哈希查找的方式
要比单链表更快,因为相当于将一个链表拆分成了255个短的链表。
6作业:
1.完成分步实现字符设备驱动的实例
2.通过设备文件识别设备
echo 1 > /dev/myled0 led1亮 (open write close)
echo 0 > /dev/myled0 led1灭 (open write close)
echo 1 > /dev/myled1 led2亮 (open write close)
echo 0 > /dev/myled1 led2灭 (open write close)
echo 1 > /dev/myled2 led3亮 (open write close)
echo 0 > /dev/myled2 led3灭 (open write close)
myled0 myled1 myled2
-------------------------------
myled(驱动)
-------------------------------
led1 led2 led3
6.1根据设备文件识别设备
echo 1 > /dev/myled0 led1亮 (open write close)
echo 0 > /dev/myled0 led1灭 (open write close)
echo 1 > /dev/myled1 led2亮 (open write close)
echo 0 > /dev/myled1 led2灭 (open write close)
echo 1 > /dev/myled2 led3亮 (open write close)
echo 0 > /dev/myled2 led3灭 (open write close)
myled0 myled1 myled2
240,0 240,1 240,2
---------------------------------------------------------------------------------------------
只要在存在一个文件,内核中一定会对应的inode结构体,这个结构体就是用来描述文件的
inode0 inode1 inode2
i_rdev i_rdev i_rdev
---------------------------------------------------------------------------------------------
myled(驱动):
myled_open(struct inode* inode, struct file* filp)
{
int curno;
curno = MINOR(inode->i_rdev);
//curno=0
//curno=1
//curno=2
}
---------------------------------------------------------------------------------------------
led1 led2 led3
myled.c
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#define CNAME "myled"
#define COUNT 3
struct cdev* cdev;
int major = 238;
int minor = 0;
char kbuf[128] = { 0 };
struct class* cls;
struct device* dev;
int curno;
enum {
LED1,
LED2,
LED3
};
int myled_open(struct inode* inode, struct file* filp)
{
curno = MINOR(inode->i_rdev);
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
ssize_t myled_read(struct file* filp,
char __user* ubuf, size_t size, loff_t* offs)
{
int ret;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (size > sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_to_user(ubuf, kbuf, size);
if (ret) {
printk("copy data to user error\n");
return -EINVAL;
}
return size;
}
ssize_t myled_write(struct file* filp,
const char __user* ubuf, size_t size, loff_t* offs)
{
int ret;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (size > sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_from_user(kbuf, ubuf, size);
if (ret) {
printk("copy data from user error\n");
return -EINVAL;
}
// echo "1" > /dev/myled0
switch (curno) {
case LED1:
kbuf[0] == '1' ? printk("LED1 ON\n") : printk("LED1 OFF\n");
break;
case LED2:
kbuf[0] == '1' ? printk("LED2 ON\n") : printk("LED2 OFF\n");
break;
case LED3:
kbuf[0] == '1' ? printk("LED3 ON\n") : printk("LED3 OFF\n");
break;
}
return size;
}
int myled_close(struct inode* inode, struct file* filp)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
const struct file_operations fops = {
.open = myled_open,
.read = myled_read,
.write = myled_write,
.release = myled_close,
};
static int __init myled_init(void)
{
int ret, i;
dev_t devno;
// 1.分配对象
cdev = cdev_alloc();
if (cdev == NULL) {
printk("alloc cdev memory error\n");
ret = -ENOMEM;
goto ERR1;
}
// 2.对象初始化
cdev_init(cdev, &fops);
// 3.设备号的申请
if (major == 0) {
//动态申请
ret = alloc_chrdev_region(&devno, minor, COUNT, CNAME);
if (ret) {
printk("dynamic:request device number error\n");
goto ERR2;
}
major = MAJOR(devno);
minor = MINOR(devno);
} else {
//静态指定
ret = register_chrdev_region(MKDEV(major, minor), COUNT, CNAME);
if (ret) {
printk("static:request device number error\n");
goto ERR2;
}
}
// 4.字符设备驱动的注册
ret = cdev_add(cdev, MKDEV(major, minor), COUNT);
if (ret) {
printk("register chardev error\n");
goto ERR3;
}
// 5.自动创建设备节点
cls = class_create(THIS_MODULE, CNAME);
if (IS_ERR(cls)) {
printk("class create error\n");
ret = PTR_ERR(cls);
goto ERR4;
}
for (i = minor; i < minor + 3; i++) {
dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);
if (IS_ERR(dev)) {
printk("device create error\n");
ret = PTR_ERR(dev);
goto ERR5;
}
}
return 0; /*!!!!!!!!!!!!!!不要忘记!!!!!!!!!!!!!!!!!!!!!!!*/
ERR5:
for (--i; i >= minor; i--) {
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
ERR4:
cdev_del(cdev);
ERR3:
unregister_chrdev_region(MKDEV(major, minor), COUNT);
ERR2:
kfree(cdev);
ERR1:
return ret;
}
static void __exit myled_exit(void)
{
int i;
//注销设备节点
for (i = minor; i < minor + 3; i++) {
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
//字符设备驱动的注销
cdev_del(cdev);
//设备号释放
unregister_chrdev_region(MKDEV(major, minor), COUNT);
//释放cdev的内存空间
kfree(cdev);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
, COUNT);
if (ret) {
printk(“register chardev error\n”);
goto ERR3;
}
// 5.自动创建设备节点
cls = class_create(THIS_MODULE, CNAME);
if (IS_ERR(cls)) {
printk("class create error\n");
ret = PTR_ERR(cls);
goto ERR4;
}
for (i = minor; i < minor + 3; i++) {
dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);
if (IS_ERR(dev)) {
printk("device create error\n");
ret = PTR_ERR(dev);
goto ERR5;
}
}
return 0; /*!!!!!!!!!!!!!!不要忘记!!!!!!!!!!!!!!!!!!!!!!!*/
ERR5:
for (–i; i >= minor; i–) {
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
ERR4:
cdev_del(cdev);
ERR3:
unregister_chrdev_region(MKDEV(major, minor), COUNT);
ERR2:
kfree(cdev);
ERR1:
return ret;
}
static void __exit myled_exit(void)
{
int i;
//注销设备节点
for (i = minor; i < minor + 3; i++) {
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
//字符设备驱动的注销
cdev_del(cdev);
//设备号释放
unregister_chrdev_region(MKDEV(major, minor), COUNT);
//释放cdev的内存空间
kfree(cdev);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE(“GPL”);