驱动开发 作业2

使用 ioctl 替换 write/read 控制 LED、蜂鸣器、马达、风扇,并使用 udev 来自动创建设备文件

  1. 完整代码目录,请看这个仓库
  2. 依然使用之前 ARM 课程中的 common 中的结构体
  3. 代码都差不多,就贴个 led.c ,用户空间测试代码 test.c 和头文件 head.h
  4. 不同设备 devcls 以及 major 命名要不同
  5. FSMP1A 扩展板引脚:
    • LED1,LED2,LED3:PE10PF10PE8
    • 蜂鸣器:PB6
    • 马达:PF6
    • 风扇:PE9
#include "linux/leds.h"
#include 
#include 
#include 
#include 
#include 
#include "head.h"

struct class *cls;
struct device *dev;

int major; // 用于保存主设备号
char kbuf[128] = { 0 };

unsigned int *vir_gpioe;
unsigned int *vir_gpiof;
unsigned int *vir_rcc;

// 封装操作方法
int mycdev_open(struct inode *inode, struct file *file)
{
	printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
	return 0;
}

long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	switch (cmd) {
	case LED_ON:
		switch (arg) {
		case 1: // LED1
			((gpio_t *)vir_gpioe)->ODR |= (0x1 << 10);
			break;
		case 2: // LED2
			((gpio_t *)vir_gpiof)->ODR |= (0x1 << 10);
			break;
		case 3: // LED3
			((gpio_t *)vir_gpioe)->ODR |= (0x1 << 8);
			break;
		}
		break;
	case LED_OFF:
		switch (arg) {
		case 1: // LED1
			((gpio_t *)vir_gpioe)->ODR &= (~(0x1 << 10));
			break;
		case 2: // LED2
			((gpio_t *)vir_gpiof)->ODR &= (~(0x1 << 10));
			break;
		case 3: // LED3
			((gpio_t *)vir_gpioe)->ODR &= (~(0x1 << 8));
			break;
		}
		break;
	}
	return 0;
}

int mycdev_close(struct inode *inode, struct file *file)
{
	printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
	return 0;
}

// 定义操作方法结构体变量并完成初始化
struct file_operations fops = {
	.open = mycdev_open,
	.unlocked_ioctl = mycdev_ioctl,
	.release = mycdev_close,
};

int all_led_init(void)
{
	// 地址映射
	vir_gpioe = ioremap((unsigned int)GPIOE, sizeof(gpio_t));
	if (vir_gpioe == NULL) {
		printk("GPIOE结构体映射表失败\n");
		return -EFAULT;
	}
	vir_gpiof = ioremap((unsigned int)GPIOF, sizeof(gpio_t));
	if (vir_gpioe == NULL) {
		printk("GPIOF结构体映射表失败\n");
		return -EFAULT;
	}
	vir_rcc = ioremap((unsigned int)RCC, sizeof(rcc_t));
	if (vir_rcc == NULL) {
		printk("RCC结构体映射表失败\n");
		return -EFAULT;
	}
	printk("寄存器地址映射成功\n");
	// 寄存器初始化
	((rcc_t *)vir_rcc)->MP_AHB4ENSETR |= (1 << 4) | (1 << 5);
	// LED1 初始化
	((gpio_t *)vir_gpioe)->MODER &= (~(0x3 << 20));
	((gpio_t *)vir_gpioe)->MODER |= (0x1 << 20);
	((gpio_t *)vir_gpioe)->ODR &= (~(0x1 << 10));
	// LED2 初始化
	((gpio_t *)vir_gpiof)->MODER &= (~(0x3 << 20));
	((gpio_t *)vir_gpiof)->MODER |= (0x1 << 20);
	((gpio_t *)vir_gpiof)->ODR &= (~(0x1 << 10));
	// LED3 初始化
	((gpio_t *)vir_gpioe)->MODER &= (~(0x3 << 16));
	((gpio_t *)vir_gpioe)->MODER |= (0x1 << 16);
	((gpio_t *)vir_gpioe)->ODR &= (~(0x1 << 8));
	return 0;
}

static int __init mycdev_init(void)
{
	int i;
	//字符设备驱动注册
	major = register_chrdev(0, "myleddev", &fops);
	if (major < 0) {
		printk("字符设备驱动注册失败\n");
		return major;
	}
	printk("字符设备驱动注册成功major=%d\n", major);
	// 向上提交目录
	cls = class_create(THIS_MODULE, "myledcls");
	if (IS_ERR(cls)) {
		printk("创建类失败\n");
		return -EFAULT;
	}
	// 向上提交设备节点信息
	for (i = 0; i < 3; i++) {
		dev = device_create(cls, NULL, MKDEV(major, i), NULL, "led%d",
				    i);
		if (IS_ERR(dev)) {
			printk("创建设备节点失败\n");
			return -EFAULT;
		}
	}
	//映射物理寄存器
	all_led_init();
	return 0;
}

static void __exit mycdev_exit(void)
{
  int i;
	//取消寄存器地址映射
	iounmap(vir_gpioe);
	iounmap(vir_gpiof);
	iounmap(vir_rcc);
  for(i = 0; i < 3; i++) {
    device_destroy(cls, MKDEV(major, i));
  }
  class_destroy(cls);
	//字符设备驱动的注销
	unregister_chrdev(major, "myleddev");
}

module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "head.h"

int main(int argc, char const *argv[])
{
	int a, b;
	char buf[128] = { 0 };
	int led_fd = open("/dev/led0", O_RDWR);
	if (led_fd < 0) {
		printf("打开设备文件失败\n");
		exit(-1);
	}
	int fan_fd = open("/dev/fan", O_RDWR);
  if (fan_fd < 0) {
    printf("打开设备文件失败\n");
    exit(-1);
  }
	int bee_fd = open("/dev/bee", O_RDWR);
  if (bee_fd < 0) {
    printf("打开设备文件失败\n");
    exit(-1);
  }
	int motor_fd = open("/dev/motor", O_RDWR);
  if (motor_fd < 0) {
    printf("打开设备文件失败\n");
    exit(-1);
  }
	while (1) {
		//从终端读取
		printf("请选择要控制的设备\n");
		printf("0(灯) 1(风扇) 2(蜂鸣器) 3(马达) >");

		scanf("%d", &a);
		if (a == 0) {
			printf("请输入要实现的功能\n");
			printf("0(关灯) 1(开灯) >");
			scanf("%d", &a);

			printf("请输入要控制的灯\n");
			printf("1(LED1) 2(LED2) 3(LED3)>");
			scanf("%d", &b);
			if (a == 1) {
				ioctl(led_fd, LED_ON, b);
			} else if (a == 0) {
				ioctl(led_fd, LED_OFF, b);
			}
		} else if (a == 1) {
			printf("请输入要实现的功能\n");
			printf("0(关风扇) 1(开风扇) >");
			scanf("%d", &a);
			if (a == 1) {
				ioctl(fan_fd, FAN_ON);
			} else if (a == 0) {
				ioctl(fan_fd, FAN_OFF);
			}
		} else if (a == 2) {
      printf("请输入要实现的功能\n");
      printf("0(关蜂鸣器) 1(开蜂鸣器) >");
      scanf("%d", &a);
      if (a == 1) {
        ioctl(bee_fd, BEE_ON);
      } else if (a == 0) {
        ioctl(bee_fd, BEE_OFF);
      }
    } else if (a == 3) {
      printf("请输入要实现的功能\n");
      printf("0(关马达) 1(开马达) >");
      scanf("%d", &a);
      if (a == 1) {
        ioctl(motor_fd, MOTOR_ON);
      } else if (a == 0) {
        ioctl(motor_fd, MOTOR_OFF);
      }
    }
	}
	close(led_fd);
	close(fan_fd);
	close(bee_fd);
  close(motor_fd);

	return 0;
}
#ifndef __HEAD_H__
#define __HEAD_H__

#include "rcc.h"
#include "gpio.h"

#define LED_ON _IOW('l', 1, int) //开灯
#define LED_OFF _IOW('l', 0, int) //关灯

#define FAN_ON _IO('f', 0)
#define FAN_OFF _IO('f', 1)

#define BEE_ON _IO('b', 0)
#define BEE_OFF _IO('b', 1)

#define MOTOR_ON _IO('m', 0)
#define MOTOR_OFF _IO('m', 1)
#endif

驱动开发 作业2_第1张图片

你可能感兴趣的:(驱动开发)