X210 basic character driver to create device association and virtual internal address mapping

First of all, it is clear that the role of the driver is similar to providing an interface for the application program to associate the device and operating the device to open, read and write, etc. In the summary of the previous article, I learned the development framework of the driver, and based on this, the system device For the association with the driver, the first is to create a device file. This device file is placed under /dev/ in the root directory. You can view the devices in the /dev/ directory to know what devices are currently running on the system. You can see that there are Many device files are intercepted here

Our next step is to create a device file to associate with our driver for the application to call
The key information of the device file is: device number = major device number + minor device number, use ls -l to view the device file, you can get the major and minor device numbers corresponding to the device file.
Use mknod to create a device file: mknod /dev/xxx c major device number minor device number
It should be noted that the major and minor device numbers of the device file we created cannot be used by other device files. Here we create a device file. The major device number is 250 and the secondary device number is 0.

mknod /dev/test c 250 0

You can view the device number by cat /proc/devices
Then install our driver file
insmod module.ko
What is the link between our device files and drivers here?
The answer is the major device number. The major device number of the device file we created is 250, and the device number in the driver file is also 250. The device file and the driver file can be associated with the major device number. The next step is to call the device driver in the application. Interface, corresponding to open, write, etc.
Note that the interface called by the application should be consistent with the interface provided by the device

// Customize a file_operations structure variable and fill it
static const struct file_operations test_fops = {
	.owner		= THIS_MODULE,				// Convention, just write it directly
	
	.open		= test_chrdev_open,			// The actual call when the application open opens the device in the future
	.release	= test_chrdev_release,		// This is the function corresponding to this .open
	.write 		= test_chrdev_write,
	.read		= test_chrdev_read,
};

application

#define FILE "/dev/test" // The name of the device file created by mknod just now

char buf[100];

int main(void)
{
	int fd = -1;
	int i = 0;
	
	fd = open(FILE, O_RDWR);
	if (fd < 0)
	{
		printf("open %s error.\n", FILE);
		return -1;
	}
	printf("open %s success..\n", FILE);
	while (1)
	{
		memset(buf, 0 , sizeof(buf));
		printf("please enter on | off \n");
		scanf("%s", buf);
		if (!strcmp(buf, "on"))
		{
			write(fd, "1", 1);
		}
		else if (!strcmp(buf, "off"))
		{
			write(fd, "0", 1);
		}
		else if (!strcmp(buf, "flash"))
		{
			for (i=0; i<3; i++)
			{
				write(fd, "1", 1);
				sleep(1);
				write(fd, "0", 1);
				sleep(1);
			}
		}	
		else if (!strcmp(buf, "quit"))
		{
			break;
		}
	}
	// close file
	close(fd);//The actual call is the release in the file_operations structure
	return 0;
}

Results of the
Brief summary:
a driver file
Create a device file in the /dev/ directory
Driver files and device files are associated with major device numbers
The application program achieves the purpose of calling the driver function by calling the program interface

The second summary: virtual address mapping
The concept of virtual memory will not be discussed here, and Baidu has an explanation.
There are two types of virtual addresses: static virtual addresses and dynamic ones. There is another thing called virtual memory. Don't get confused.
The virtual static address can be understood as the determined address table, which has always existed and is static, while the virtual dynamic address is dynamic and needs to be applied and released by calling the function. These two methods have their own advantages and disadvantages.
virtual static address
For Samsung's X5PV210, mainly check these files under the kernel

The main mapping table is located at: arch/arm/plat-s5p/include/plat/map-s5p.h
 The virtual address base address is defined in: arch/arm/plat-samsung/include/plat/map-base.h
GPIO The relevant master mapping tables are located at: arch/arm/mach-s5pv210/include/mach/regs-gpio.h
GPIO The specific register definitions are located at: arch/arm/mach-s5pv210/include/mach/gpio-bank.h

map-s5p.h file content

#ifndef __ASM_PLAT_MAP_S5P_H
#define __ASM_PLAT_MAP_S5P_H __FILE__

#define S5P_VA_CHIPID		S3C_ADDR(0x00700000)
#define S5P_VA_GPIO		S3C_ADDR(0x00500000)
#define S5P_VA_SYSTIMER		S3C_ADDR(0x01200000)
#define S5P_VA_SROMC		S3C_ADDR(0x01100000)
#define S5P_VA_AUDSS		S3C_ADDR(0X01600000)

#define S5P_VA_UART0		(S3C_VA_UART + 0x0)
#define S5P_VA_UART1		(S3C_VA_UART + 0x400)
#define S5P_VA_UART2		(S3C_VA_UART + 0x800)
#define S5P_VA_UART3		(S3C_VA_UART + 0xC00)

#define S3C_UART_OFFSET		(0x400)

#define VA_VIC(x)		(S3C_VA_IRQ + ((x) * 0x10000))
#define VA_VIC0			VA_VIC(0)
#define VA_VIC1			VA_VIC(1)
#define VA_VIC2			VA_VIC(2)
#define VA_VIC3			VA_VIC(3)

#endif /* __ASM_PLAT_MAP_S5P_H */

map-base.h file content

#ifndef __ASM_PLAT_MAP_H
#define __ASM_PLAT_MAP_H __FILE__

/* Fit all our registers in at 0xF4000000 upwards, trying to use as
 * little of the VA space as possible so vmalloc and friends have a
 * better chance of getting memory.
 *
 * we try to ensure stuff like the IRQ registers are available for
 * an single MOVS instruction (ie, only 8 bits of set data)
 */

#define S3C_ADDR_BASE	(0xFD000000)

#ifndef __ASSEMBLY__
#define S3C_ADDR(x)	((void __iomem __force *)S3C_ADDR_BASE + (x))
#else
#define S3C_ADDR(x)	(S3C_ADDR_BASE + (x))
#endif

#define S3C_VA_IRQ	S3C_ADDR(0x00000000)	/* irq controller(s) */
#define S3C_VA_SYS	S3C_ADDR(0x00100000)	/* system control */
#define S3C_VA_MEM	S3C_ADDR(0x00200000)	/* memory control */
#define S3C_VA_TIMER	S3C_ADDR(0x00300000)	/* timer block */
#define S3C_VA_WATCHDOG	S3C_ADDR(0x00400000)	/* watchdog */
#define S3C_VA_OTG	S3C_ADDR(0x00E00000)	/* OTG */
#define S3C_VA_OTGSFR	S3C_ADDR(0x00F00000)	/* OTG PHY */
#define S3C_VA_UART	S3C_ADDR(0x01000000)	/* UART */

/* This is used for the CPU specific mappings that may be needed, so that
 * they do not need to directly used S3C_ADDR() and thus make it easier to
 * modify the space for mapping.
 */
#define S3C_ADDR_CPU(x)	S3C_ADDR(0x00500000 + (x))

#endif /* __ASM_PLAT_MAP_H */

The other two will not be shown, when the register is operated on the external device

define register

#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>		// arch/arm/mach-s5pv210/include/mach/gpio-bank.h
#include <linux/string.h>

#define GPJ0CON		S5PV210_GPJ0CON
#define GPJ0DAT		S5PV210_GPJ0DAT

#define rGPJ0CON	*((volatile unsigned int *)GPJ0CON)
#define rGPJ0DAT	*((volatile unsigned int *)GPJ0DAT)

operation register

rGPJ0CON = 0x11111111;
rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));		// Bright

It can achieve the purpose of operating external devices through virtual static address operation registers.

virtual dynamic address
It needs to be applied by calling the function, and the dynamic mapping table needs to be released after use to save resources
Define the register address, and apply for the physical address used for dynamic application of virtual address mapping, and apply through request_mem_region(GPJ0CON_PA, 4, "GPJ0CON")

#define GPJ0CON_PA 0xe0200240 //physical address
#define GPJ0DAT_PA 	0xe0200244

unsigned int *pGPJ0CON;
unsigned int *pGPJ0DAT;

Apply for mapping virtual address

pGPJ0CON = ioremap(GPJ0CON_PA, 4);
pGPJ0DAT = ioremap(GPJ0DAT_PA, 4);

operation register

*pGPJ0CON = 0x11111111;
*pGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));		// Bright

Contact Mapping

iounmap(pGPJ0CON);
iounmap(pGPJ0DAT);
release_mem_region(GPJ0CON_PA, 4);
release_mem_region(GPJ0DAT_PA, 4);

Note that this is only used for early learning. Due to the limited number of soc pins, the general pins need to be multiplexed in time to realize the operation of peripherals with different functions, so the operation of registers cannot affect the status of other peripherals. This involves judging whether the io is free, and then applying and operating, which will be recorded later, so we will go here first.

Tags: Linux bash

Posted by ExpendableDecoy on Fri, 07 Oct 2022 02:56:52 +0530