十、用户态中的平台驱动
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