作业
3.1 实验现象
crw------- 1 root root 236, 0 Apr 11 15:55 /dev/myled0
crw------- 1 root root 236, 1 Apr 11 15:55 /dev/myled1
crw------- 1 root root 236, 2 Apr 11 15:55 /dev/myled2在串口工具,输入如下命令实现控制对应的LED灯进行工作echo 1 > /dev/myled0 ========> led1灯点亮echo 0 > /dev/myled0 ========> led1灯熄灭echo 1 > /dev/myled1 ========> led2灯点亮echo 0 > /dev/myled1 ========> led2灯熄灭echo 1 > /dev/myled2 ========> led3灯点亮echo 0 > /dev/myled2 ========> led3灯熄灭
3.2 编程思路
struct inode {dev_t i_rdev; //设备号};int myled_open(struct inode *inode, struct file *file)
{//获取次设备的值,将次设备号的值,通过私有数据传参,传递给write函数return 0;
}
--------------------------------------------------------------------------------
ssize_t myled_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff)
{//判断私有数据的值,也就是次设备号的值,决定操作哪一盏灯return 0;
}
3.3 编程要求
- 分步注册字符设备驱动
- 自动创建设备节点
- 三盏灯地址映射
- 三盏灯初始化
- 私有数据传参
- 用户空间和内核空间数据传输
- 控制灯亮灭
demo.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/device.h>
#include "myled.h"struct cdev *cdev;
#if 1
unsigned int major = 0;
#else
unsigned int major = 500;
#endif
unsigned int minor = 0;
unsigned int count = 3;
#define CNAME "myled"
struct class *cls;
struct device *device;char kbuf[128] = "";
unsigned int *rcc_virt = NULL;
gpio_t *gpioe_virt = NULL;
gpio_t *gpiof_virt = NULL;#define LED1_ON (gpioe_virt->ODR |= (0x1 << 10))
#define LED1_OFF (gpioe_virt->ODR &= (~(0x1 << 10)))#define LED2_ON (gpiof_virt->ODR |= (0x1 << 10))
#define LED2_OFF (gpiof_virt->ODR &= (~(0x1 << 10)))#define LED3_ON (gpioe_virt->ODR |= (0x1 << 8))
#define LED3_OFF (gpioe_virt->ODR &= (~(0x1 << 8)))struct inode
{dev_t i_rdev; // 设备号
};int myled_open(struct inode *inode, struct file *file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}ssize_t myled_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}ssize_t myled_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff)
{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 form user is error\n");return -EIO;}// kbuf[0] = 0 ==> 操作LED1 ==> kbuf[1] = 0 灯熄灭 kbuf[1] = 1 灯点亮// kbuf[0] = 1 ==> 操作LED2 ==> kbuf[1] = 0 灯熄灭 kbuf[1] = 1 灯点亮// kbuf[0] = 2 ==> 操作LED3 ==> kbuf[1] = 0 灯熄灭 kbuf[1] = 1 灯点亮switch (kbuf[0]){case LED1:kbuf[1] == 1 ? LED1_ON : LED1_OFF;break;case LED2:kbuf[1] == 1 ? LED2_ON : LED2_OFF;break;case LED3:kbuf[1] == 1 ? LED3_ON : LED3_OFF;break;}return size; //!!!!!!!!!!!return 0;
}int myled_close(struct inode *inode, struct file *file)
{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 demo_init(void)
{int ret;int i;dev_t devno;// 分配对象cdev = cdev_alloc();if (cdev == NULL){printk("cdev alloc is error\n");ret = -ENOMEM;goto ERR1;}// 对象初始化cdev_init(cdev, &fops);if (major > 0){ // 静态指定设备号ret = register_chrdev_region(MKDEV(major, minor), count, CNAME);if (ret){printk("register chrdev region is error\n");ret = -EIO;goto ERR2;}}else{ // 动态指定设备号ret = alloc_chrdev_region(&devno, minor, count, CNAME);if (ret){printk("alloc chrdev region is error\n");ret = -EIO;goto ERR2;}major = MAJOR(devno); // 通过设备号,获取到主设备号的值minor = MINOR(devno); // 通过设备号,获取到次设备号的值}// 注册ret = cdev_add(cdev, MKDEV(major, minor), count);if (ret){printk("cdev add is error\n");ret = -EIO;goto ERR3;}// 向上层提交目录信息cls = class_create(THIS_MODULE, CNAME);if (IS_ERR(cls)){ret = PTR_ERR(cls);goto ERR4;}for (i = 0; i < count; i++){// 向上层提交设备节点信息device = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);if (IS_ERR(device)){ret = PTR_ERR(device);goto ERR5;}}// 自动创建设备节点// 注册字符设备驱动major = register_chrdev(0, CNAME, &fops);if (major < 0){printk("register chrdev is error\n");return -EIO;}printk("major = %d\n", major); // 打印主设备号的值// 向上层提交目录信息cls = class_create(THIS_MODULE, CNAME);if (IS_ERR(cls)){return PTR_ERR(cls);}// 向上层提交设备节点信息device = device_create(cls, NULL, MKDEV(major, 0), NULL, CNAME);if (IS_ERR(device)){return PTR_ERR(device);}//// rcc物理地址映射rcc_virt = ioremap(RCC_MP_AHB4ENSETR_PHY, 4);if (rcc_virt == NULL){printk("rcc ioremap is error\n");return -EIO;}// GPIOE组寄存器物理地址映射gpioe_virt = ioremap(GPIOE, sizeof(gpio_t));if (gpioe_virt == NULL){printk("gpioe ioremap is error\n");return -EIO;}// GPIOF组寄存器物理地址映射gpiof_virt = ioremap(GPIOF, sizeof(gpio_t));if (gpiof_virt == NULL){printk("gpiof ioremap is error\n");return -EIO;}// LED1灯初始化 PE10*rcc_virt |= (0x1 << 4); // 使能GPIOE组控制器gpioe_virt->MODER &= (~(0x3 << 20)); // 设置PE10引脚为输出模式gpioe_virt->MODER |= (0x1 << 20);gpioe_virt->ODR &= (~(0x1 << 10)); // 设置PE10引脚输出低电平// LED2灯初始化 PF10*rcc_virt |= (0x1 << 5); // 使能GPIOF组控制器gpiof_virt->MODER &= (~(0x3 << 20)); // 设置PF10引脚为输出模式gpiof_virt->MODER |= (0x1 << 20);gpiof_virt->ODR &= (~(0x1 << 10)); // 设置PF10引脚输出低电平// LED3灯初始化 PE8gpioe_virt->MODER &= (~(0x3 << 16)); // 设置PE8引脚为输出模式gpioe_virt->MODER |= (0x1 << 16);gpioe_virt->ODR &= (~(0x1 << 8)); // 设置PE8引脚输出低电平return 0;return 0; //!!!!!!!!!!!!!!!!!!ERR5:// 创建三个设备节点时,第一个设备节点和第二个设备节点创建成功,第三个设备节点创建失败// 需要将第一个设备节点和第二个设备节点创建成功,需要进行释放for (--i; i >= 0; 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 demo_exit(void)
{int i = 0;for (i = 0; i < count; i++){device_destroy(cls, MKDEV(major, i));}class_destroy(cls);cdev_del(cdev);unregister_chrdev_region(MKDEV(major, minor), count);kfree(cdev);// 取消地址映射iounmap(rcc_virt);iounmap(gpioe_virt);iounmap(gpiof_virt);// 注销字符设备驱动unregister_chrdev(major, CNAME);
}module_init(demo_init); // 指定入口地址
module_exit(demo_exit); // 指定出口地址
MODULE_LICENSE("GPL"); // 遵循GPL协议
test.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#include <unistd.h>#include <string.h>
int main(int argc, const char *argv[])
{int fd = -1;char buf[128] = {};fd = open("/dev/myled",O_RDWR);if(fd == -1){perror("open is error");return -1;}while(1){buf[0] = 0;write(fd,buf,sizeof(buf)); //将用户空间中buf中的内容,写入到内核空间kbufbuf[1] = 1;write(fd,buf,sizeof(buf)); //将用户空间中buf中的内容,写入到内核空间kbufsleep(1);buf[1] = 0;write(fd,buf,sizeof(buf)); //将用户空间中buf中的内容,写入到内核空间kbufsleep(1);buf[0] = 1;write(fd,buf,sizeof(buf)); //将用户空间中buf中的内容,写入到内核空间kbufbuf[1] = 1;write(fd,buf,sizeof(buf)); //将用户空间中buf中的内容,写入到内核空间kbufsleep(1);buf[1] = 0;write(fd,buf,sizeof(buf)); //将用户空间中buf中的内容,写入到内核空间kbufsleep(1);buf[0] = 2;write(fd,buf,sizeof(buf)); //将用户空间中buf中的内容,写入到内核空间kbufbuf[1] = 1;write(fd,buf,sizeof(buf)); //将用户空间中buf中的内容,写入到内核空间kbufsleep(1);buf[1] = 0;write(fd,buf,sizeof(buf)); //将用户空间中buf中的内容,写入到内核空间kbufsleep(1);}close(fd);return 0;
}
test.h
#ifndef __MYLED_H__
#define __MYLED_H__#define RCC_MP_AHB4ENSETR_PHY 0x50000A28enum{LED1,LED2,LED3,
};typedef struct {volatile unsigned int MODER; // 0x00volatile unsigned int OTYPER; // 0x04volatile unsigned int OSPEEDR; // 0x08volatile unsigned int PUPDR; // 0x0Cvolatile unsigned int IDR; // 0x10 volatile unsigned int ODR; // 0x14volatile unsigned int BSRR; // 0x18volatile unsigned int LCKR; // 0x1C volatile unsigned int AFRL; // 0x20 volatile unsigned int AFRH; // 0x24volatile unsigned int BRR; // 0x28volatile unsigned int res;volatile unsigned int SECCFGR; // 0x30}gpio_t;#define GPIOE (0x50006000)
#define GPIOF (0x50007000)#endif
不足之处:
1.分步注册字符设备驱动后是否需要再搞一次自动创建设备节点,再搞一次是否有意义?
2.如何获取串口输入的内容并将其存储在容器中传输到主机?