前面我們介紹了Linux設(shè)備模型、平臺設(shè)備驅(qū)動、設(shè)備樹(device tree)、GPIO子系統(tǒng)以及pinctrl子系統(tǒng)等,大家看這篇文章之前需要提前知道的基礎(chǔ)都在這篇文章中:
Linux設(shè)備模型、平臺設(shè)備驅(qū)動、設(shè)備樹(device tree)、GPIO子系統(tǒng)以及pinctrl子系統(tǒng)介紹
有部分函數(shù)沒有涉及到的最后會講解。
一、配置連接說明
我們做控制led燈的時候用的是下面三個管腳:
控制LED燈連接實(shí)圖:
二、更新設(shè)備樹
(1)將led燈引腳添加到pinctrl子系統(tǒng)
將我們的引腳添加到 igkboard.dts 下的 &iomuxc 節(jié)點(diǎn)下:
pinctrl_my_gpio_leds: my-gpio-leds {
fsl,pins = <
MX6UL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x17059 /* led run */
MX6UL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x17059
MX6UL_PAD_JTAG_MOD__GPIO1_IO10 0x17059
>;
};
引腳定義都是在文件::~/imx6ull/imx6ull/bsp/kernel/linux-imx/arch/arm/boot/dts
下可以查看:
wangdengtao@wangdengtao-virtual-machine:~/imx6ull/imx6ull/bsp/kernel/linux-imx/arch/arm/boot/dts$ cat imx6ul-pinfunc.h
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2014 - 2015 Freescale Semiconductor, Inc.
*/
#ifndef __DTS_IMX6UL_PINFUNC_H
#define __DTS_IMX6UL_PINFUNC_H
/*
* The pin function ID is a tuple of
* <mux_reg conf_reg input_reg mux_mode input_val>
*/
#define MX6UL_PAD_BOOT_MODE0__GPIO5_IO10 0x0014 0x02a0 0x0000 5 0
#define MX6UL_PAD_BOOT_MODE1__GPIO5_IO11 0x0018 0x02a4 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x0020 0x02ac 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x003c 0x02c8 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER0__GPIO5_IO00 0x001c 0x02a8 0x0000 5 0
#define MX6UL_PAD_JTAG_MOD__GPIO1_IO10 0x0044 0x02d0 0x0000 5 0
(2)設(shè)備樹中添加LDE燈的設(shè)備樹節(jié)點(diǎn)
將我們的 my_leds 設(shè)備節(jié)點(diǎn)添加在 igkbosrd.dts 的根節(jié)點(diǎn)下:
my_leds {
compatible = "my-gpio-leds"; /*設(shè)置“compatible”屬性值,與led的平臺驅(qū)動做匹配*/
pinctrl-names = "default"; /*定義引腳狀態(tài)*/
pinctrl-0 = <&pinctrl_my_gpio_leds>; /*指定LED燈的引腳pinctrl信息*/
status = "okay";
led-gpios = <&gpio5 8 GPIO_ACTIVE_HIGH>,/*指定引腳使用的哪個GPIO 引腳名字= <&GPIO組 GPIO編號 有效電平>*/
<&gpio5 1 GPIO_ACTIVE_HIGH>,
<&gpio1 10 GPIO_ACTIVE_HIGH>;
default-state = "off";
};
(3)編譯更新設(shè)備樹
添加完成之后我們需要去 linux-imx 文件夾下執(zhí)行 make dtbs 編譯一下我們的設(shè)備樹,然后將開發(fā)板上如下的文件路徑下的 igkboard.dtb 以及 zImage(linux下的zImage文件再/bootl路徑下) 修改。
wangdengtao@wangdengtao-virtual-machine:~/imx6ull/imx6ull/bsp/kernel/linux-imx$ make dtbs
root@igkboard:~# find / -name zImage
/run/media/mmcblk1p1/zImage
root@igkboard:~# find / -name igkboard.dtb
/run/media/mmcblk1p1/igkboard.dtb
替換之后執(zhí)行 sudo reboot 即可。
使用新的設(shè)備樹重新啟動之后正常情況下會在開發(fā)板的 “/proc/device-tree” 目錄下生成 “my_leds” 設(shè)備樹節(jié)點(diǎn)。如下所示。
root@igkboard:~# cd /proc/device-tree/
root@igkboard:/proc/device-tree# ls
'#address-cells' 3p3v backlight-lcd clock-di0 compatible leds mqs panel pxp_v4l2 regulator@0 soc w1
'#size-cells' __symbols__ chosen clock-di1 cpus memory@80000000 my_leds pmu regulator-peri-3v3 reserved-memory sound-mqs
1p8v aliases clock-cli clock-osc keys model name pwm-buzzer regulator-sd1-vmmc serial-number timer
進(jìn)入節(jié)點(diǎn)文件我們可以看到我們設(shè)置的gpio子系統(tǒng)的屬性:
root@igkboard:/proc/device-tree# cd my_leds/
root@igkboard:/proc/device-tree/my_leds# ls
compatible default-state led-gpios name pinctrl-0 pinctrl-names status
三、驅(qū)動開發(fā)與測試
(1)編寫設(shè)備驅(qū)動代碼
代碼中涉及到的字符設(shè)備驅(qū)動不了解的可以參考這篇文章:Linux下字符設(shè)備驅(qū)動開發(fā)以及流程介紹
/*************************************************************************
> File Name: led_gpio.c
> Author: WangDengtao
> Mail: 1799055460@qq.com
> Created Time: 2023年03月21日 星期二 13時55分02秒
************************************************************************/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
/*如果沒有定義DEV_MAJOR就設(shè)置設(shè)備號為0,采用動態(tài)申請,如果有則使用宏定義的設(shè)備號*/
//#define DEV_MAJOR 88
#ifndef DEV_MAJOR
#define DEV_MAJOR 0
#endif
#define PLATDRV_MAGIC 0x60 //魔術(shù)字
#define LED_OFF _IO (PLATDRV_MAGIC, 0x18)
#define LED_ON _IO (PLATDRV_MAGIC, 0x19)
#define DEV_NAME "my_led" /*宏定義設(shè)備的名字*/
int dev_major = DEV_MAJOR;
/*led設(shè)備初始化*/
struct led_device {
dev_t devid; /* 設(shè)備號 */
struct cdev *cdev; /*cdev結(jié)構(gòu)體*/
struct class *class; /*定義一個class用于創(chuàng)建類 */
struct device *device; /*設(shè)備 */
struct device_node *node; /* led設(shè)備節(jié)點(diǎn) */
struct gpio_desc *led_gpio1,*led_gpio2,*led_gpio3; /*led燈GPIO描述符 */
}led_dev;
/*字符設(shè)備操作函數(shù)集,open函數(shù)*/
static int led_open(struct inode *inode, struct file *file)
{
file->private_data = &led_dev; //設(shè)置私有數(shù)據(jù)
printk(KERN_DEBUG "/dev/led%d opened.\n", led_dev.devid);
return 0;
}
/*字符設(shè)備操作函數(shù)集,close函數(shù)*/
static int led_release(struct inode *inode, struct file *file)
{
printk(KERN_DEBUG "/dev/led%d opened.\n", led_dev.devid);
return 0;
}
static void print_led_help(void)
{
printk("Follow is the ioctl() command for LED driver:\n");
printk("Turn LED on command : %u\n", LED_ON);
printk("Turn LED off command : %u\n", LED_OFF);
}
/*字符設(shè)備操作函數(shù)集,ioctl函數(shù)*/
static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
if(cmd == LED_OFF)/* variable case */
{
if(arg == 1)
{
gpiod_set_value(led_dev.led_gpio1, 0);
}
else if(arg == 2)
{
gpiod_set_value(led_dev.led_gpio2, 0);
}
else if(arg == 3)
{
gpiod_set_value(led_dev.led_gpio3, 0);
}
else
{
printk("arg argument 1 2 3\n");
return -EINVAL;
}
}
else if(cmd == LED_ON)
{
if(arg == 1)
{
gpiod_set_value(led_dev.led_gpio1, 1);
}
else if(arg == 2)
{
gpiod_set_value(led_dev.led_gpio2, 1);
}
else if(arg == 3)
{
gpiod_set_value(led_dev.led_gpio3, 1);
}
else
{
printk("arg argument 1 2 3\n");
return -EINVAL;
}
}
else
{
printk("%s driver don't support ioctl command=%d\n", DEV_NAME, cmd);
print_led_help();
return -EINVAL;
}
return 0;
}
/*字符設(shè)備操作函數(shù)集*/
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
.unlocked_ioctl = led_ioctl,
};
/*驅(qū)動安裝函數(shù)*/
static int led_probe(struct platform_device * pdev)
{
int result= 0;
/*獲取led的設(shè)備樹節(jié)點(diǎn),該函數(shù)適用于只有一個gpio,index為0*/
//led_dev.led_gpio = gpiod_get(&pdev -> dev, "led", 0);
led_dev.led_gpio1 = gpiod_get_index(&pdev -> dev, "led", 0, GPIOD_OUT_HIGH);
led_dev.led_gpio2 = gpiod_get_index(&pdev -> dev, "led", 1, GPIOD_OUT_HIGH);
led_dev.led_gpio3 = gpiod_get_index(&pdev -> dev, "led", 2, GPIOD_OUT_HIGH);
if(IS_ERR(led_dev.led_gpio1))
{
printk("gpiod request failure\n");
return -1;
}
/*設(shè)置GPIO的方向?yàn)檩敵鰻顟B(tài),默認(rèn)為低電平*/
result = gpiod_direction_output(led_dev.led_gpio1, 0);
gpiod_direction_output(led_dev.led_gpio2, 0);
gpiod_direction_output(led_dev.led_gpio3, 0);
if(0 != result )
{
printk("gpiod direction output set failure\n");
return result;
}
/*字符設(shè)備驅(qū)動注冊的流程一:分配主次設(shè)備號,這里不僅支持靜態(tài)指定,也支持動態(tài)申請*/
/*靜態(tài)申請主次設(shè)備號*/
if(0 != dev_major)
{
led_dev.devid = MKDEV(dev_major, 0);//將主設(shè)備號dev_major和從設(shè)備號0分配給devno變量
result = register_chrdev_region(led_dev.devid, 1, DEV_NAME);//請求分配一個設(shè)備號,名字為DEV_NAME(chardev),設(shè)備號是:88 0
}
/*動態(tài)申請*/
else
{
result = alloc_chrdev_region(&led_dev.devid, 0, 1, DEV_NAME);//求分配一個名字為wangdengtao_dev的設(shè)備號,從設(shè)備號為0,保存到devid變量中
dev_major = MAJOR(led_dev.devid);//獲取設(shè)備號
}
/*失敗后的處理結(jié)果,總規(guī)上面只執(zhí)行一次,所以直接在外面判斷就可*/
if(result < 0)
{
printk(KERN_ERR " %s chardev can't use major %d\n", DEV_NAME, dev_major);
return -result;
}
printk("%s driver use major %d\n", DEV_NAME, dev_major);
/*字符串設(shè)備驅(qū)動流程三:分配cdev結(jié)構(gòu)體,使用動態(tài)申請的方式*/
/*
內(nèi)核在內(nèi)部使用類型struct cdev的結(jié)構(gòu)體來代表字符設(shè)備。在內(nèi)核調(diào)用你的設(shè)備操作之前,你必須分配
一個這樣的結(jié)構(gòu)體并注冊給linux內(nèi)核,在這個結(jié)構(gòu)體里有對于這個設(shè)備進(jìn)行操作的函數(shù),具體定義在
file_operation結(jié)構(gòu)體中。
*/
if(NULL == (led_dev.cdev = cdev_alloc()))
{
printk(KERN_ERR "%s driver can't alloc for the cdev\n", DEV_NAME);
unregister_chrdev_region(led_dev.devid, 1);//釋放掉設(shè)備號
return -ENOMEM;
}
/*字符設(shè)備驅(qū)動流程三:分配cdev結(jié)構(gòu)體,綁定主次設(shè)備號,fops到cdev結(jié)構(gòu)體中,并且注冊到linux內(nèi)核*/
led_dev.cdev -> owner = THIS_MODULE; /*.owner這表示誰擁有這個驅(qū)動程序*/
cdev_init(led_dev.cdev, &led_fops);/*初始化設(shè)備*/
result = cdev_add(led_dev.cdev, led_dev.devid, 1); /*將字符設(shè)備注冊進(jìn)內(nèi)核*/
if(0 != result)
{
printk(KERN_INFO "%s driver can't register cdev:result = %d\n", DEV_NAME, result);
goto ERROR;
}
printk(KERN_INFO "%s driver can register cdev:result = %d\n", DEV_NAME, result);
/*自動創(chuàng)建設(shè)備類型、/dev設(shè)備節(jié)點(diǎn)*/
led_dev.class = class_create(THIS_MODULE, DEV_NAME); /*創(chuàng)建設(shè)備類型sys/class/chrdev*/
if (IS_ERR(led_dev.class))
{
printk("%s driver create class failure\n", DEV_NAME);
result = -ENOMEM;
goto ERROR;
}
/*/dev/chrdev 注冊這個設(shè)備節(jié)點(diǎn)*/
led_dev.device = device_create(led_dev.class, NULL, led_dev.devid, NULL, DEV_NAME);
if(IS_ERR(led_dev.device))
{
result = -ENOMEM;//返回錯誤碼,應(yīng)用空間strerror查看
goto ERROR;
}
return 0;
ERROR:
printk(KERN_ERR" %s driver installed failure.\n", DEV_NAME);
cdev_del(led_dev.cdev);
unregister_chrdev_region(led_dev.devid, 1);
return result;
}
static int led_remove(struct platform_device *pdev)
{
gpiod_set_value(led_dev.led_gpio1, 0); //低電平關(guān)閉燈
gpiod_set_value(led_dev.led_gpio2, 0); //低電平關(guān)閉燈
gpiod_set_value(led_dev.led_gpio3, 0); //低電平關(guān)閉燈
gpiod_put(led_dev.led_gpio1); //釋放gpio
gpiod_put(led_dev.led_gpio2); //釋放gpio
gpiod_put(led_dev.led_gpio3); //釋放gpio
cdev_del(led_dev.cdev); //刪除cdev
unregister_chrdev_region(led_dev.devid, 1);//釋放設(shè)備號
device_destroy(led_dev.class, led_dev.devid);//注銷設(shè)備
class_destroy(led_dev.class); //注銷類
return 0;
}
static const struct of_device_id leds_match_table[] = {
{.compatible = "my-gpio-leds"},
{/* sentinel */},
};
MODULE_DEVICE_TABLE(of, leds_match_table);
/*內(nèi)核中使用platform_driver結(jié)構(gòu)體來描述平臺驅(qū)動*/
static struct platform_driver gpio_led_driver =
{
.probe = led_probe, //安裝驅(qū)動的時候會執(zhí)行的函數(shù)
.remove = led_remove, //驅(qū)動卸載的時候會執(zhí)行的函數(shù)
.driver = { //描述驅(qū)動的屬性
.name = "my_led", //name域
.owner = THIS_MODULE, //使用者,一般都是THIS_MODULE
.of_match_table = leds_match_table, //驅(qū)動能夠兼容的設(shè)備類型
},
};
/*入口函數(shù)*/
static int __init platdrv_led_init(void)
{
int rv;
/*
當(dāng)我們初始化了platform_driver之后,通過platform_driver_register()函數(shù)來注冊我們的平臺驅(qū)動;
成功注冊了一個平臺驅(qū)動后,就會在/sys/bus/platform/driver目錄下生成一個新的目錄項(xiàng).
成功: 0
失?。?負(fù)數(shù)
*/
rv = platform_driver_register(&gpio_led_driver);
if(rv < 0)
{
printk(KERN_ERR "%s:%d: Can't register platform driver %d \n", __FUNCTION__, __LINE__, rv);
return rv;
}
printk("Regist LED Platform Driver successfully!\n ");
return 0;
}
/*出口函數(shù)*/
static void __exit platdrv_led_exit(void)
{
printk("%s: %d remove LED platform driver\n", __FUNCTION__, __LINE__);
/*卸載的驅(qū)動模塊時,需要注銷掉已注冊的平臺驅(qū)動*/
platform_driver_unregister(&gpio_led_driver);
}
/*調(diào)用函數(shù) module_init 來聲明 xxx_init 為驅(qū)動入口函數(shù),當(dāng)加載驅(qū)動的時候 xxx_init函數(shù)就會被調(diào)用.*/
module_init(platdrv_led_init);
/*調(diào)用函數(shù)module_exit來聲明xxx_exit為驅(qū)動出口函數(shù),當(dāng)卸載驅(qū)動的時候xxx_exit函數(shù)就會被調(diào)用.*/
module_exit(platdrv_led_exit);
/*添加LICENSE和作者信息,是來告訴內(nèi)核,該模塊帶有一個自由許可證;沒有這樣的說明,在加載模塊的時內(nèi)核會“抱怨”.*/
MODULE_LICENSE("Dual BSD/GPL");//許可 GPL、GPL v2、Dual MPL/GPL、Proprietary(專有)等,沒有內(nèi)核會提示.
MODULE_AUTHOR("WangDengtao");//作者
MODULE_VERSION("V1.0");//版本
(2)編寫驅(qū)動測試代碼
/*************************************************************************
> File Name: led_gpio_test.c
> Author: WangDengtao
> Mail: 1799055460@qq.com
> Created Time: 2023年03月23日 星期四 10時46分40秒
************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#define LED_CNT 1
#define DEVNAME_LEN 30
#define PLATDRV_MAGIC 0x60
#define LED_OFF _IO (PLATDRV_MAGIC, 0x18)
#define LED_ON _IO (PLATDRV_MAGIC, 0x19)
static void msleep(unsigned long ms)
{
struct timeval tv;
tv.tv_sec = ms/1000;
tv.tv_usec = (ms%1000)*1000;
select(0, NULL, NULL, NULL, &tv);
}
int main(int argc, char **argv)
{
int fd[LED_CNT];
char dev_name[DEVNAME_LEN];
memset(dev_name, 0, sizeof(dev_name));
snprintf(dev_name, sizeof(dev_name), "/dev/my_led");
fd[LED_CNT] = open(dev_name, O_RDWR, 0755);
if(fd[LED_CNT] < 0)
{
printf("file %s open failure!\n", dev_name);
goto err;
}
printf("open fd[%d] successfully.\n", fd[LED_CNT]);
while(1)
{
ioctl(fd[LED_CNT], LED_ON, 1);
msleep(500);
ioctl(fd[LED_CNT], LED_OFF, 1);
ioctl(fd[LED_CNT], LED_ON, 2);
msleep(500);
ioctl(fd[LED_CNT], LED_OFF, 2);
ioctl(fd[LED_CNT], LED_ON, 3);
msleep(500);
ioctl(fd[LED_CNT], LED_OFF, 3);
msleep(500);
}
close(fd[LED_CNT]);
return 0;
err:
close(fd[LED_CNT]);
return -1;
}
(3)Makefile
同時編譯驅(qū)動文件以及測試文件,編譯運(yùn)行之后我們可以看見可執(zhí)行文件以及.ko文件。
KERNAL_DIR ?= /home/wangdengtao/imx6ull/imx6ull/bsp/kernel/linux-imx
PWD :=$(shell pwd)
obj-m := led_gpio.o
CC=arm-linux-gnueabihf-gcc
APP_NAME=led_gpio_test
all:
$(MAKE) -C $(KERNAL_DIR) M=$(PWD) modules
@${CC} ${APP_NAME}.c -o ${APP_NAME}
@make clear
clear:
@rm -f *.o *.cmd *.mod *.mod.c
@rm -rf *~ core .depend .tmp_versions Module.symvers modules.order -f
@rm -f .*ko.cmd .*.o.cmd .*.o.d
@rm -f *.unsigned
clean:
@rm -f *.ko
@rm -f ${APP_NAME}
wangdengtao@wangdengtao-virtual-machine:~/wangdengtao/driver/arm$ make
make -C /home/wangdengtao/imx6ull/imx6ull/bsp/kernel/linux-imx M=/home/wangdengtao/wangdengtao/driver/arm modules
make[1]: 進(jìn)入目錄“/home/wangdengtao/imx6ull/imx6ull/bsp/kernel/linux-imx”
CC [M] /home/wangdengtao/wangdengtao/driver/arm/led_gpio.o
MODPOST /home/wangdengtao/wangdengtao/driver/arm/Module.symvers
CC [M] /home/wangdengtao/wangdengtao/driver/arm/led_gpio.mod.o
LD [M] /home/wangdengtao/wangdengtao/driver/arm/led_gpio.ko
make[1]: 離開目錄“/home/wangdengtao/imx6ull/imx6ull/bsp/kernel/linux-imx”
make[1]: 進(jìn)入目錄“/home/wangdengtao/wangdengtao/driver/arm”
make[1]: 離開目錄“/home/wangdengtao/wangdengtao/driver/arm”
wangdengtao@wangdengtao-virtual-machine:~/wangdengtao/driver/arm$ ls
led_gpio.c led_gpio.ko led_gpio_test led_gpio_test.c Makefile
將我們的可執(zhí)行文件以及.ko文件上傳到開發(fā)板:
root@igkboard:~# tftp -gr led_gpio.ko 192.168.137.8
root@igkboard:~# tftp -gr led_gpio_test 192.168.137.8
root@igkboard:~# ls
led_gpio.ko led_gpio_test
四、結(jié)果展示
安裝我們的驅(qū)動,可以看見在 /dev 路徑下生成的設(shè)備樹文件 my_led。
root@igkboard:~# insmod led_gpio.ko
root@igkboard:~# lsmod
Module Size Used by
led_gpio 16384 0
rtl8188fu 999424 0
imx_rngc 16384 0
rng_core 20480 1 imx_rngc
secvio 16384 0
error 20480 1 secvio
root@igkboard:~# ls -l /dev/my_led
crw------- 1 root root 243, 0 Mar 25 08:49 /dev/my_led
執(zhí)行我們的測試代碼,我們可以看見我們的led燈隔5毫秒閃爍了:
root@igkboard:~# ./led_gpio_test
open fd[3] successfully.
最后卸載我們的驅(qū)動:
root@igkboard:~# rmmod led_gpio
root@igkboard:~# lsmod
Module Size Used by
rtl8188fu 999424 0
imx_rngc 16384 0
rng_core 20480 1 imx_rngc
secvio 16384 0
error 20480 1 secvio
五、ioctl接口講解
大部分驅(qū)動需要除了讀寫設(shè)備的能力,還需要有通過設(shè)備驅(qū)動進(jìn)行各種硬件控制的能力。
ioctl 驅(qū)動函數(shù):
int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
- inode:和 filp 指針是對應(yīng)應(yīng)用程序傳遞的文件描述符 fd 的值, 和傳遞給 open 方法的相同參數(shù)。
- cmd:參數(shù)從用戶那里不改變地傳下來,并且可選的參數(shù)。
- arg:參數(shù)以一個 unsigned long 的形式傳遞, 不管它是否由用戶給定為一個整數(shù)或一個指針。
為了保證 cmd 命令的唯一性(類似于現(xiàn)實(shí)中的身份證)。
wangdengtao@wangdengtao-virtual-machine:~$ cat /opt/TuxamitoSoftToolchains/arm-arm1176jzfssf-linux-gnueabi/gcc-4.6.4/arm-arm1176jzfssf-linux-gnueabi/sysroot/usr/include/asm-generic/ioctl.h
#ifndef _ASM_GENERIC_IOCTL_H
#define _ASM_GENERIC_IOCTL_H
/* ioctl command encoding: 32 bits total, command in lower 16 bits,
* size of the parameter structure in the lower 14 bits of the
* upper 16 bits.
* Encoding the size of the parameter structure in the ioctl request
* is useful for catching programs compiled with old versions
* and to avoid overwriting user space outside the user buffer area.
* The highest 2 bits are reserved for indicating the ``access mode''.
* NOTE: This limits the max parameter size to 16kB -1 !
*/
/*
* The following is for compatibility across the various Linux
* platforms. The generic ioctl numbering scheme doesn't really enforce
* a type field. De facto, however, the top 8 bits of the lower 16
* bits are indeed used as a type field, so we might just as well make
* this explicit here. Please be sure to use the decoding macros
* below from now on.
*/
#define _IOC_NRBITS 8
#define _IOC_TYPEBITS 8
/*
* Let any architecture override either of the following before
* including this file.
*/
#ifndef _IOC_SIZEBITS
# define _IOC_SIZEBITS 14
#endif
#ifndef _IOC_DIRBITS
# define _IOC_DIRBITS 2
#endif
#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1)
#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1)
#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1)
#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1)
#define _IOC_NRSHIFT 0
#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS)
#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS)
#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS)
/*
* Direction bits, which any architecture can choose to override
* before including this file.
*/
#ifndef _IOC_NONE
# define _IOC_NONE 0U
#endif
#ifndef _IOC_WRITE
# define _IOC_WRITE 1U
#endif
#ifndef _IOC_READ
# define _IOC_READ 2U
#endif
#define _IOC(dir,type,nr,size) \
(((dir) << _IOC_DIRSHIFT) | \
((type) << _IOC_TYPESHIFT) | \
((nr) << _IOC_NRSHIFT) | \
((size) << _IOC_SIZESHIFT))
#define _IOC_TYPECHECK(t) (sizeof(t))
/* used to create numbers */
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOR_BAD(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))
#define _IOW_BAD(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
#define _IOWR_BAD(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
/* used to decode ioctl numbers.. */
#define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
#define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
#define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
#define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
/* ...and for the drivers/sound files... */
#define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT)
#define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT)
#define IOC_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT)
#define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT)
#define IOCSIZE_SHIFT (_IOC_SIZESHIFT)
#endif /* _ASM_GENERIC_IOCTL_H */
在驅(qū)動程序里, ioctl() 函數(shù)上傳送的變量 cmd 是應(yīng)用程序用于區(qū)別設(shè)備驅(qū)動程序請求處理內(nèi)容的值。cmd除了可區(qū)別數(shù)字外,還包含有助于處理的幾種相應(yīng)信息。 cmd的大小為 32位,共分 4 個域:
bit31~bit30 2位為 “區(qū)別讀寫” 區(qū),作用是區(qū)分是讀取命令還是寫入命令。
bit29~bit15 14位為 “數(shù)據(jù)大小” 區(qū),表示 ioctl() 中的 arg 變量傳送的內(nèi)存大小。
bit20~bit08 8位為 “魔數(shù)"(也稱為"幻數(shù)")區(qū),這個值用以與其它設(shè)備驅(qū)動程序的 ioctl 命令進(jìn)行區(qū)別。
bit07~bit00 8位為 “區(qū)別序號” 區(qū),是區(qū)分命令的命令順序序號。
內(nèi)核定義了 _IO() , _IOR() , IOW() 和 _IOWR() 這 4 個宏來輔助生成上面的 cmd 。下面分析 _IO() 的實(shí)現(xiàn)。
上面的代碼中可以看見_IO的定義以及_IOC的定義:
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
#define _IOC(dir,type,nr,size) \
(((dir) << _IOC_DIRSHIFT) | \
((type) << _IOC_TYPESHIFT) | \
((nr) << _IOC_NRSHIFT) | \
((size) << _IOC_SIZESHIFT))
#ifndef _IOC_NONE
# define _IOC_NONE 0U
#endif
#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) //8
#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) //16
#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) //30
#define _IOC_NRSHIFT 0
#define _IOC_NRBITS 8
#define _IOC_TYPEBITS 8
(dir) << _IOC_DIRSHIFT) dir 往左移 30 位,即移到 bit31~bit30 兩位上,得到方向(讀寫)的屬性
(size) << _IOC_SIZESHIFT) 位左移 16 位得到“數(shù)據(jù)大小”區(qū)
(type) << _IOC_TYPESHIFT) 左移 8位得到"魔數(shù)區(qū)"
(nr) << _IOC_NRSHIFT) 左移 0 位( bit7~bit0)
前面代碼中我們使用的宏定義解釋:
#define PLATDRV_MAGIC 0x60
#define LED_OFF _IO (PLATDRV_MAGIC, 0x18)
#define LED_ON _IO (PLATDRV_MAGIC, 0x19)
_IO (魔數(shù), 基數(shù)):
魔數(shù) (magic number)
魔數(shù)范圍為 0~255 。通常,用英文字符 “A” ~ “Z” 或者 “a” ~ “z” 來表示。設(shè)備驅(qū)動程序從傳遞進(jìn)來的命令獲取魔數(shù),然后與自身處理的魔數(shù)想比較,如果相同則處理,不同則不處理。魔數(shù)是拒絕誤使用的初步輔助狀態(tài)。設(shè)備驅(qū)動 程序可以通過 _IOC_TYPE (cmd) 來獲取魔數(shù)。不同的設(shè)備驅(qū)動程序最好設(shè)置不同的魔數(shù),但并不是要求絕對,也是可以使用其他設(shè)備驅(qū)動程序已用過的魔數(shù)。
基(序列號)數(shù)文章來源:http://www.zghlxwxcb.cn/news/detail-582399.html
基數(shù)用于區(qū)別各種命令。通常,從 0開始遞增,相同設(shè)備驅(qū)動程序上可以重復(fù)使用該值。例如,讀取和寫入命令中使用了相同的基數(shù),設(shè)備驅(qū)動程序也能分辨出來,原因在于設(shè)備驅(qū)動程序區(qū)分命令時 使用 switch ,且直接使用命令變量 cmd值。創(chuàng)建命令的宏生成的值由多個域組合而成,所以即使是相同的基數(shù),也會判斷為不同的命令。設(shè)備驅(qū)動程序想要從命令中獲取該基數(shù),就使用下面的宏:_IOC_NR (cmd)
文章來源地址http://www.zghlxwxcb.cn/news/detail-582399.html
到了這里,關(guān)于Linux下LED設(shè)備驅(qū)動開發(fā)(LED燈實(shí)現(xiàn)閃爍)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!