How to compile the driver statically into the kernel.
Development boards used: TQ210 's s5pv210 Development Board
Kernel version: linux-3.8.3
Compiler: ARM-LINUX-GCC 4.4.3
Relevant download Address:
First, ready to properly compile the source code to guide the Development Board (
How to achieve the correct configuration and compile a good kernel source of the link address; http://blog.csdn.net/girlkoo/article/details/8719828)
Second, under the driver directory of the kernel source kernel folder, create the Led_arm folder
mkdir Drivers/led_arm
C. Copy the driver of "led drive experiment" to the Led_arm directory (see the previous LED driver blog for details)
Copy the led.c below the folder
The following is the writing of the LED.C driver
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <asm/gpio.h>
#include <plat/gpio-cfg.h>
#define LED_ON 0x100001
#define Led_off 0x100002
Hardware Resources
struct led_resource{
int Gpio; Gpio Port
Char *name; The device name. Show in/proc/devices/
};
static struct Led_resource led_info[] = {
[0] ={
. Gpio = s5pv210_gpc0 (3),
. Name = "LED1"
},
[1] ={
. Gpio = s5pv210_gpc0 (4),
. Name = "LED2"
}
};
Software Management
static dev_t Dev; Device number
struct Cdev led_cdev;//Creating a device class
static struct class *cls; To create a device class pointer
static int Led_open (struct inode *inode, struct file *file)
{
PRINTK ("%s\n", __func__);
return 0;
}
static int led_close (struct inode *inode, struct file *file)
{
PRINTK ("%s\n", __func__);
return 0;
}
static int led_ioctl (struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
static int led_ioctl (struct file *file,
unsigned int cmd, unsigned long arg)
{
int kindex;
Copy data to kernel space
Copy_from_user (&kindex,
(int *) arg, sizeof (KINDEX));
Switch (CMD) {
Case LED_ON:
Gpio_set_value (led_info[kindex-1].gpio,1);
Break
Case Led_off:
Gpio_set_value (led_info[kindex-1].gpio,0);
Break
Default
return-1;
}
return 0;
}
static struct File_operations Led_fops = {
. Owner = This_module,
. open = Led_open,
. Release = Led_close,
. Unlocked_ioctl = Led_ioctl
};
static int led_init (void)
{
int i;
1. Assigning Character devices
Alloc_chrdev_region (&dev, 0, 1, "LEDs");
2. Initialize the character device
Cdev_init (&led_cdev, &led_fops);
3. Registering a character device
Cdev_add (&led_cdev, Dev, 1);
4. Add a Device file
4.1 Main device class
The result is: sys/class/myleds
CLS = Class_create (This_module, "myleds");
4.2 Creating a Device file
Device_create (CLS, null, dev, null, "myleds");
5. Request a GPIO Resource
for (i=0; i < array_size (led_info); i++)
{
Gpio_request (Led_info[i].gpio, led_info[i].name);
Gpio_direction_output (Led_info[i].gpio, 0);
}
return 0;
}
static void Led_exit (void)
{
int i;
Deleting device files and device classes
Device_destroy (CLS, Dev);
Class_destroy (CLS);
1. Uninstalling Character Device objects
Cdev_del (&led_cdev);
2. Releasing Gpio resources
for (i=0; i< array_size (Led_info); i++)
{
Gpio_set_value (Led_info[i].gpio, 0);
Gpio_free (Led_info[i].gpio);
}
3. Release the device number
Unregister_chrdev_region (Dev, 1);
}
Module_init (Led_init);
Module_exit (Led_exit);
Module_license ("GPL");
Since Kconfig is the configuration menu corresponding to the kernel. If you want to add a new driver to the kernel source, you can modify the Kconfig to add to our drive configuration menu, so there is a way to choose our driver.
Each config menu item must have a type definition, bool: Boolean type, TriState: Built-in, module, remove, String: String, Hex: 16, Integer: Integer
For example, config Hello_module
BOOL "Hello Test module"
BOOL type can only be selected or unchecked, tristate type of menu items are compiled into kernel module options, if you choose to compile into a kernel module, you will generate a config_hello_module=m configuration in. CONFIG, if you choose built-in, is to compile directly into the kernel effect, a config_hello_module=y configuration is generated in. config.
Below are the following actions under Led_arm:
① Add a Kconfig file
[CPP] view plain copy
1. config myled
2. TriState "Myled"
3. Default N
4. Help
5. This is a LED driver
② Add a Makefile
[CPP] view plain copy
1. obj-$ (config_ledtest) +=LED.O
Iv. Add the driver support options we have written to the kernel configuration menu.
Go back to drivers and modify the Kconfig file in the drivers directory
Add a statement on the last line
[CPP] view plain copy
1. Source "Drivers/led_arm/kconfig"
V. Modification of drivers/makefile files
Add the following statement
obj-$ (config_ledtest) +=led_arm/
Six, reconfigure the kernel
Enter in the source root directory
#make Menuconfig
At the end of the menu, you can see our newly added myled option. Here you can choose the type of kernel to compile, * for static compilation drive to the kernel, m to compile the driver into a module driver, here we will drive statically compiled into the kernel.
Make Zimage
Download the arch/arm/boot/kernel/zimage to the Development Board for testing.
The following is the preparation of the LED_TEST.C program;
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define LED_ON 0x100001
#define Led_off 0x100002
int main (int argc, char *argv[])
{
int FD;
int cmd;
if (ARGC < 3) {
printf ("usage:\n%s <on|off> <1|2>\n", argv[0]);
return-1;
}
Fd= Open ("/dev/myleds", O_RDWR);
if (FD < 0) {
printf ("Open LED device failed. \ n ");
return-1;
}
Cmd= Strtoul (argv[2], NULL, 10);
if (!strcmp (argv[1]), "on")
IOCTL (FD,LED_ON,&CMD);
else if (!strcmp (argv[1], "off"))
IOCTL (FD,LED_OFF,&CMD);
Close (FD);
return 0;
}
Summary of issues:
The following error occurs if the kernel used in the compilation process is a kernel version after linux-2.6.36 and the IOCTL is used in it.
Error:unknown field ' IOCTL ' specified in initializer
The problem is that the original IOCTL was removed after the 2.6.36 kernel and two new members were added, so an error occurred
Long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
Long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
So the modification needs to be within the file_operations of the driver's source file. The IOCTL is changed to. compat_ioctl
OK, compile through, warn me to ignore
Kernel 2.6.35 and previous versions of the struct file_operations altogether had 3 IOCTL:
Ioctl,unlocked_ioctl and Compat_ioctl
It's only Unlocked_ioctl and Compat_ioctl.
In kernel 2.6.36, the IOCTL function pointers in struct file_operations have been completely removed and replaced by Unlocked_ioctl.
But the biggest effect after the pointer function has changed is that the inode is missing from the parameter,
In kernel 2.6.36, the IOCTL function pointers in struct file_operations have been completely removed and replaced by Unlocked_ioctl.
This pointer function changed after the biggest effect is the parameter is less inode, but this is not a problem, because the user program in the IOCTL corresponding system call interface does not change, so the user program does not need to change, everything to the kernel processing, if you want to unlocked_ The following methods can be used to obtain information such as Inode in the IOCTL:
struct Inode *inode = file->f_mapping->host;
struct Block_device *bdev = inode->i_bdev;
struct Gendisk *disk = bdev->bd_disk;
fmode_t mode = file->f_mode;
struct Backing_dev_info *bdi;
Parameter change reference file_operations this structure, the most important of which is the cmd parameter, this change is very large.
For example, the parameter settings written in our driver:
IOCTL parameters used prior to Linux-2.6.36
static int led_ioctl (struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
Parameters used after Linux-2.6.36
static int led_ioctl (struct file *file,
unsigned int cmd, unsigned long arg)