Linux driver development learning notes 0-1 led module

Posted by ereimber on Thu, 13 Jan 2022 21:05:05 +0100

Learning record - based on teacher Wei Dongshan's tutorial

  1. String driven development process
    (1) Determine the main equipment number
    (2) Define your own file_operation structure
    (3) Implement the corresponding drv_open,drv_write and other functions
    (4) Write an entry function to register the driver
    (5) Write an exit function to unload the driver
  2. Steps of led development
    (1) Look at the schematic diagram to determine whether the led is on or off at GPIO port and output high and low levels
    (2) Enable: CCM is used to set the clock provided to GPIO module, Clock Controller Module, find the corresponding chip manual and change the value of the corresponding bit of the corresponding register
    (3) Set the pin operating mode to GPIO mode
    (4) Set pin direction (GPIOx_GDIR): each pin corresponds to one pin, 1-output, 0-input
    (5) Set output pin level (GPIOx_DR): each pin corresponds to one pin, 1-high level and 0-low level
    (6) Read pin level (GPIOx_PSR): each pin corresponds to one pin, 1-high level and 0-low level
  3. Driver programming
    (1) Add the header file, open the source code of the Linux kernel, open a driver at random, and copy the header file contained therein;
    (2) determining the main equipment number can be set to zero first and then call register_. Chrdev() function, which returns a valid device group;
    (3) Define file_operation structure: similarly, find a driver to imitate and define the structure you need.
static const struct file_operations led_fops = {
	.owner		= THIS_MODULE,
	.write		= led_write,
	.open		= led_open,
};

(4) Implement the corresponding driver function:

/* write function */
static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) {
	char val;
	int retu;
	/* copy_from_user: get data from app */
	retu = copy_from_user(&val, buf, 1);
	/* to set gpio register: 1/0 */
	if (val){
		/* set gpio to let led on */
		*GPIO5_DR &= ~(1<<3);
	}
	else {
		/*set gpio to let led off */
		*GPIO5_DR |= (1<<3);
	}
	 
	return 0;
}

/* open function */
static int led_open(struct inode *inode, struct file *file) {
	/* 
	* enable gpio 
	* configure gpio5_io3 as gpio
	* configure gpio5_io3 as output
	*/
	*IOMUXC_GPIO5_IO03 &= ~0xf;
	*IOMUXC_GPIO5_IO03 |= 0x5;
	
	*GPIO5_GDIR |= (1<<3);
	
	return 0;
}


(5) In the driver development, the hardware address cannot be directly used, but the actual address needs to be mapped with the ioremap() function for reuse. In this example, because the GPIO module is enabled by default and there is no need to check the level, the corresponding two steps are omitted.

/* register */
// IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 Address: 229_0000h base + 14h offset = 229_0014h   0x2290000+0x14
static volatile unsigned int *IOMUXC_GPIO5_IO03;
// gpio5 020A_C000 
// GPIO direction register     input/output   Address: Base address + 4h offset   0x20AC004
static volatile unsigned int *GPIO5_GDIR;
// GPIO data register          0/1       Address: Base address + 0h offset   0X20AC000
static volatile unsigned int *GPIO5_DR;

	/* ioremap Map actual address */
	// IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 Address: 229_0000h base + 14h offset = 229_0014h   0x2290000+0x14
	IOMUXC_GPIO5_IO03 = ioremap(0x2290000+0x14, 4);
	// gpio5 020A_C000 
	// GPIO direction register	   input/output   Address: Base address + 4h offset   0x20AC004
	GPIO5_GDIR = ioremap(0x20AC004, 4);
	// GPIO data register		   0/1		 Address: Base address + 0h offset	 0X20AC000
	GPIO5_DR = ioremap(0X20AC000, 4);

(6) Write entry function‘

/* Entry function */
static int __init led_init(void) {

	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	/* Register driver */
	major = register_chrdev(0, "yuelong_led", &led_fops);  //Receive the returned master device number for exit function destruction

	/* ioremap Map actual address */
	// IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 Address: 229_0000h base + 14h offset = 229_0014h   0x2290000+0x14
	IOMUXC_GPIO5_IO03 = ioremap(0x2290000+0x14, 4);
	// gpio5 020A_C000 
	// GPIO direction register	   input/output   Address: Base address + 4h offset   0x20AC004
	GPIO5_GDIR = ioremap(0x20AC004, 4);
	// GPIO data register		   0/1		 Address: Base address + 0h offset	 0X20AC000
	GPIO5_DR = ioremap(0X20AC000, 4);


	/* Create classes and nodes */
	led_class = class_create(THIS_MODULE, "myled");
	device_create(led_class, NULL, MKDEV(major, 0), NULL, "myled"); // Device node with / dev/myled created

	
	return 0;
}

(7) Write exit function

/* Exit function */
static void __exit led_exit(void) {

	/* Clearly map the actual address */
	iounmap(IOMUXC_GPIO5_IO03);
	iounmap(GPIO5_GDIR);
	iounmap(GPIO5_DR);
	/* Destroy nodes and classes */
	device_destroy(led_class, MKDEV(major, 0));
	class_destroy(led_class);

	/* Unregister driver */
	unregister_chrdev(major, "yuelong_led");

}

(8) Only by declaring the above entry and exit functions can we truly register and uninstall the driver, and the life GPL protocol:

// Declare the entry and exit functions of the appeal
module_init(led_init);
module_exit(led_exit);
// GPL protocol
MODULE_LICENSE("GPL");

Topics: Linux