This week, I learned how to use it. Refer to ldd3 and embedded system interface design and Linux driver development. Combined with my own development board, I developed the LED driver program, writing a program on your own is really different from reading a book. Many mistakes were made in the process, but the program was finally completed. I'm very happy!
Hardware Platform: tq2440
Kernel version: 2.6.30.4
1. Hardware Introduction
Next, we will first introduce the wiring of my Development Board. My Development Board is the tq2440 of tianyin.com and its led wiring:
Among them, nled_1 is connected to the gpb5 pin of S3C2440, and the others are successively connected to gpb6, gpb7, gpb8, and LEDs are lit at low power levels. These conditions are enough for the write driver.
2. LED driver
Divide the program into several parts to explain them separately. First, it is the first part. This part is mainly the header file and the pre-definition for debugging. At the same time, some parameters are defined. By default, the primary and secondary device numbers are both 0 and there are 4 LEDs, at the same time, a key structure is provided. The structure of my led device contains the PIN information of the LED, the status of the LED, the semaphore, And the cdev structure of the LEDs.
#include <linux/module.h> #include <linux/init.h>#include <mach/regs-gpio.h> /*S3C2410_GPB5_OUTP and S3C2410_GPB5*/#include <linux/kernel.h> /*printk*/#include <linux/fs.h>#include <linux/types.h> /*size_t and atomic_t*/#include <linux/cdev.h> /*cdev*/#include <mach/hardware.h> /*s3c2410_gpio_cfgpin and s3c2410_gpio_setpin*/#include <asm/uaccess.h> /*copy_*_user*//*for debug use*/#undef DEBUG //#define DEBUG#ifdef DEBUG#define PRINTK printk("Success!\n")#else#define PRINTK#endif/*LED start pin*/#define LEDSTARTPIN S3C2410_GPB5int yjpLED_major = 0;int yjpLED_minor = 0;int nr_LED = 4;module_param(yjpLED_major, int, S_IRUGO);module_param(yjpLED_minor, int, S_IRUGO);module_param(nr_LED, int, S_IRUGO);struct yjpLED{unsigned int LEDpin;unsigned char status;struct semaphore sem;struct cdev cdev;};struct yjpLED *yjpLEDs;
The last part is some protocol information and author information, but the most important part is the initialization function and exit function.
module_init(yjpLED_init);module_exit(yjpLED_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Yjp");
Next, let's look at my initialization functions.
int __init yjpLED_init(void){int result, i;dev_t dev;if(yjpLED_major){dev = MKDEV(yjpLED_major, yjpLED_minor);result = register_chrdev_region(dev, nr_LED, "yjpLED");}else{result = alloc_chrdev_region(&dev, yjpLED_minor, nr_LED, "yjpLED");yjpLED_major=MAJOR(dev);}if(result < 0){printk(KERN_ALERT "yjpLED: can't get major %d\n", yjpLED_major);return result; }yjpLEDs = kmalloc(nr_LED * sizeof(struct yjpLED), GFP_KERNEL);if (!yjpLEDs) {result = -ENOMEM;goto fail; memset(yjpLEDs, 0, nr_LED * sizeof(struct yjpLED));}for(i = 0; i < nr_LED; i++){yjpLEDs[i].LEDpin= LEDSTARTPIN + i; yjpLEDs[i].status = 1;}for(i = 0; i < nr_LED; i++){s3c2410_gpio_cfgpin(yjpLEDs[i].LEDpin, S3C2410_GPB5_OUTP << (i*2));s3c2410_gpio_setpin(yjpLEDs[i].LEDpin, yjpLEDs[i].status);init_MUTEX(&yjpLEDs[i].sem);setup_yjpLED_cdev(&yjpLEDs[i].cdev, i);}if (!yjpLEDs) {result = -ENOMEM;goto fail; }printk(KERN_ALERT "yjpLED init!\n");return result;fail:yjpLED_exit();return result;}
The function completes the following tasks:
1. Register a device and assign it a device number. After successful registration, you can see the device under/proc/devices.
2. Allocate space for the device structure and initialize it to 0.
3. hardware initialization.
4. cdev structure initialization.
The cdev structure Initialization is completed by the following functions:
void setup_yjpLED_cdev(struct cdev *cdev, int index){int err, devno = MKDEV(yjpLED_major, yjpLED_minor + index);cdev_init(cdev, &yjpLED_fops);(*cdev).owner = THIS_MODULE;err = cdev_add(cdev, devno, 1);PRINTK;if(err){printk(KERN_ALERT "Error %d adding yjpLED%d", err, index);}}
Initialize necessary members of cdev and activate cdev. The LED is already "active. The following shows the exit function:
void __exit yjpLED_exit(void){int i, devno = MKDEV(yjpLED_major, yjpLED_minor);if(yjpLEDs){for(i = 0; i < nr_LED; i++){cdev_del(&yjpLEDs[i].cdev);PRINTK;}kfree(yjpLEDs);}printk(KERN_ALERT "yjpLED exit!\n");unregister_chrdev_region(devno, nr_LED);}
Delete the cdev structure of each device and log out of the device.
Then let's take a look at the key struct, file_operations.
struct file_operations yjpLED_fops = {.owner = THIS_MODULE,.write = yjpLED_write,.open = yjpLED_open,.release = yjpLED_release,};
It defines the device's open, read, and write operations. Open operation functions:
int yjpLED_open(struct inode *inode, struct file *filp){struct yjpLED *dev;dev = container_of(inode->i_cdev, struct yjpLED, cdev);filp->private_data = dev;PRINTK;return 0;}
Store the pointer of the opened device in the private_data member that represents the opened file pointer filp for other operation functions.
Release Operation:
int yjpLED_release(struct inode *inode, struct file *filp){filp->private_data = NULL;return 0;}
Release the content allocated by open in filp-> private_data.
Write operation:
ssize_t yjpLED_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos){ssize_t retval;struct yjpLED *dev;dev = filp->private_data;if(down_interruptible(&dev->sem))return -ERESTARTSYS;if(copy_from_user(&dev->status, buf, sizeof(dev->status))){printk(KERN_ALERT "after copy but error!\n");retval = -EFAULT;goto out;}retval = sizeof(dev->status);dev->status =~ (dev->status) & 1;s3c2410_gpio_setpin(dev->LEDpin, dev->status);PRINTK;out:up(&dev->sem);return retval;}
The main tasks are as follows:
1. Obtain the device semaphore to prevent competing states.
2. obtain data from the user space. If the data is successful, set the corresponding PIN. Otherwise, release the semaphore and exit.
The above is the entire program. Copy it to the drivers/Char/in the kernel directory, modify the kconfig file and MAKEFILE file in the directory, and compile the yjpled module. ko, copy it to the/lib/directory of the file system. Be sure to modify the permission to executable.
The scull_load script in ldd3 has been modified to facilitate the loading of modules, copied to the file system/lib directory, and the permission can be modified to execute. The script content is as follows:
#!/bin/sh# $Id: scull_load,v 1.4 2004/11/03 06:19:49 rubini Exp $module="yjpLED"device="yjpLED"mode="664"group="root"# invoke insmod with all arguments we got# and use a pathname, as insmod doesn't look in . by default/sbin/insmod ./$module.ko $* || exit 1# retrieve major numbermajor=$(awk "\$2==\"$module\" {print \$1}" /proc/devices)# Remove stale nodes and replace them, then give gid and perms# Usually the script is shorter, it's scull that has several devices in it.rm -f /dev/${device}[0-3]mknod /dev/${device}0 c $major 0mknod /dev/${device}1 c $major 1mknod /dev/${device}2 c $major 2mknod /dev/${device}3 c $major 3chgrp $group /dev/${device}[0-3] chmod $mode /dev/${device}[0-3]
Start the Development Board, enter the/lib directory, load the module, and run the test. Use the echo command to write data to the device file. Write the singular number to light up the corresponding led, and write the double number to close the corresponding led.
For example, echo-N 1>/dev/yjpled2 lights up the third led (starting from 0)
Echo-N 2>/dev/yjpled2, the third LED is disabled.