嵌入式Linux设备驱动程序开发指南10(用户态中的平台驱动)——读书笔记

十、用户态中的平台驱动

10.1 概述

一般Linux设备驱动运行在内核态,但也允许运行在用户态,即UIO。
UIO的优点是减少频繁切换模式带来的系统开销。
Linux在drivers/uio目录下提供两种不同UIO设备驱动程序:
1、

driver/uio.c		//通过mmap()函数将设备内存映射到进程地址空间
uio_pdrv_genirq  //最小化内核态驱动程序,用户提供的内核驱动程序

2、
UIO平台设备驱动程序

drivers/uio_pdev_genirq.c

10.2 设备树

/ {
	model = "Atmel SAMA5D2 Xplained";
	compatible = "atmel,sama5d2-xplained", "atmel,sama5d2", "atmel,sama5";

	chosen {
		stdout-path = "serial0:115200n8";
	};
	
	UIO {
		compatible = "arrow,UIO";
		reg = <0xFC038000 0x4000>;	
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_led_gpio_default>;
	};

10.3 led_sam_UIO_platform.c代码

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/uio_driver.h>

static struct uio_info the_uio_info;

static int __init my_probe(struct platform_device *pdev)
{
	int ret_val;
	struct resource *r;
	struct device *dev = &pdev->dev;
	void __iomem *g_ioremap_addr;

	dev_info(dev, "platform_probe enter\n");
	
	/* get our first memory resource from device tree */
	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!r) {
		dev_err(dev, "IORESOURCE_MEM, 0 does not exist\n");
		return -EINVAL;
	}
	dev_info(dev, "r->start = 0x%08lx\n", (long unsigned int)r->start);
	dev_info(dev, "r->end = 0x%08lx\n", (long unsigned int)r->end);

	/* ioremap our memory region and get virtual address */
	g_ioremap_addr = devm_ioremap(dev, r->start, resource_size(r));
	if (!g_ioremap_addr) {
		dev_err(dev, "ioremap failed \n");
		return -ENOMEM;
	}
	
	/* initialize uio_info struct uio_mem array */
	the_uio_info.name = "led_uio";
	the_uio_info.version = "1.0";
	the_uio_info.mem[0].memtype = UIO_MEM_PHYS;
	the_uio_info.mem[0].addr = r->start; /* physical address needed for the kernel user mapping */
	the_uio_info.mem[0].size = resource_size(r);
	the_uio_info.mem[0].name = "demo_uio_driver_hw_region";
	the_uio_info.mem[0].internal_addr = g_ioremap_addr; /* virtual address for internal driver use */

	/* register the uio device */
	ret_val = uio_register_device(&pdev->dev, &the_uio_info);
	if (ret_val != 0) {
		dev_info(dev, "Could not register device \"led_uio\"...");
	}
	return 0;
}

static int __exit my_remove(struct platform_device *pdev)
{
	uio_unregister_device(&the_uio_info);
	dev_info(&pdev->dev, "platform_remove exit\n");

	return 0;
}

static const struct of_device_id my_of_ids[] = {
	{ .compatible = "arrow,UIO"},
	{},
};

MODULE_DEVICE_TABLE(of, my_of_ids);

static struct platform_driver my_platform_driver = {
	.probe = my_probe,
	.remove = my_remove,
	.driver = {
		.name = "UIO",
		.of_match_table = my_of_ids,
		.owner = THIS_MODULE,
	}
};

module_platform_driver(my_platform_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR(" ");
MODULE_DESCRIPTION("This is a UIO platform driver that turns the LED on/off \
		   without using system calls");

10.4 UIO_app.c代码

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>

#define BUFFER_LENGHT 128
#define GPIO4_GDIR_offset 0x04
#define GPIO_DIR_MASK 1<<29
#define GPIO_DATA_MASK 1<<29

#define PIO_SODR1_offset 0x50
#define PIO_CODR1_offset 0x54
#define PIO_CFGR1_offset 0x44
#define PIO_MSKR1_offset 0x40

#define PIO_PB0_MASK (1 << 0)
#define PIO_PB5_MASK (1 << 5)
#define PIO_PB6_MASK (1 << 6)
#define PIO_CFGR1_MASK (1 << 8)

#define PIO_MASK_ALL_LEDS (PIO_PB0_MASK | PIO_PB5_MASK | PIO_PB6_MASK)

#define UIO_SIZE "/sys/class/uio/uio0/maps/map0/size"

int main()
{
	int ret, devuio_fd;
	unsigned int uio_size;
	void *temp;
	void *demo_driver_map;
	char sendstring[BUFFER_LENGHT];
	char *led_on = "on";
	char *led_off = "off";
	char *Exit = "exit";

	printf("Starting led example\n");
	devuio_fd = open("/dev/uio0", O_RDWR | O_SYNC);
	if (devuio_fd < 0){
		perror("Failed to open the device");
		exit(EXIT_FAILURE);
	}

	/* read the size that has to be mapped */
	FILE *size_fp = fopen(UIO_SIZE, "r");
	fscanf(size_fp, "0x%08X", &uio_size);
	fclose(size_fp);

	/* do the mapping */
	demo_driver_map = mmap(NULL, uio_size, PROT_READ|PROT_WRITE, MAP_SHARED, devuio_fd, 0);
	if(demo_driver_map == MAP_FAILED) {
		perror("devuio mmap");
		close(devuio_fd);
		exit(EXIT_FAILURE);
	}

	temp = demo_driver_map + PIO_MSKR1_offset;
	*(int *)temp |= PIO_MASK_ALL_LEDS;

	/* select output */
	temp = demo_driver_map + PIO_CFGR1_offset;
	*(int *)temp |= PIO_CFGR1_MASK;

	/* clear all the leds */
	temp = demo_driver_map + PIO_SODR1_offset;
	*(int *)temp |= PIO_MASK_ALL_LEDS;

	/* control the LED */
	do {
		printf("Enter led value: on, off, or exit :\n");
		scanf("%[^\n]%*c", sendstring);
		if(strncmp(led_on, sendstring, 3) == 0)
		{
			temp = demo_driver_map + PIO_CODR1_offset;
			*(int *)temp |= PIO_PB0_MASK;
		}
		else if(strncmp(led_off, sendstring, 2) == 0)
		{
			temp = demo_driver_map + PIO_SODR1_offset;
			*(int *)temp |= PIO_PB0_MASK;
		}
		else if(strncmp(Exit, sendstring, 4) == 0)
		printf("Exit application\n");

		else {
			printf("Bad value\n");
			temp = demo_driver_map + PIO_SODR1_offset;
			*(int *)temp |= PIO_PB0_MASK;
			return -EINVAL;
		}

	} while(strncmp(sendstring, "exit", strlen(sendstring)));

	ret = munmap(demo_driver_map, uio_size);
	if(ret < 0) {
		perror("devuio munmap");
		close(devuio_fd);
		exit(EXIT_FAILURE);
	}

	close(devuio_fd);
	printf("Application termined\n");
	exit(EXIT_SUCCESS);
}

10.5 测试调试

insmod led_sam_UIO_platform.ko
./UIO_app
rmmod led_sam_UIO_platform.ko

感谢阅读,祝君成功!
-by aiziyou

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jack.Jia

感谢打赏!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值