目录
4.1 编译设备树文件jz2440.dts得到jz2440.dtb
a. 使用"总线设备驱动模型"编写的驱动程序分为platform_device和platform_driver两部分
platform_device : 指定硬件资源, 来自.c文件
platform_driver : 根据与之匹配的platform_device获得硬件资源, 并分配/设置/注册file_operations
b. 实际上platform_device也可以来自设备树文件.dts
dts文件被编译为dtb文件,
dtb文件会传给内核,
内核会解析dtb文件, 构造出一系列的device_node结构体,
device_node结构体会转换为platform_device结构体
所以: 我们可以在dts文件中指定资源, 不再需要在.c文件中设置platform_device结构体
c. "来自dts的platform_device结构体" 与 "我们写的platform_driver" 的匹配过程:
"来自dts的platform_device结构体"里面有成员".dev.of_node", 它里面含有各种属性, 比如 compatible, reg, pin
"我们写的platform_driver"里面有成员".driver.of_match_table", 它表示能支持哪些来自于dts的 platform_device
如果"of_node中的compatible" 跟 "of_match_table中的compatible" 一致, 就表示匹配成功, 则调 用 platform_driver中的probe函数;
在probe函数中, 可以继续从of_node中获得各种属性来确定硬件资源
dts - device tree source // 设备树源文件
dtb - device tree blob // 设备树二进制文件, 由dts编译得来
blob - binary large object
- // SPDX-License-Identifier: GPL-2.0
- /*
- * SAMSUNG SMDK2440 board device tree source
- *
- * Copyright (c) 2018 weidongshan@qq.com
- * dtc -I dtb -O dts -o jz2440.dts jz2440.dtb
- */
-
- #define S3C2410_GPA(_nr) ((0<<16) + (_nr))
- #define S3C2410_GPB(_nr) ((1<<16) + (_nr))
- #define S3C2410_GPC(_nr) ((2<<16) + (_nr))
- #define S3C2410_GPD(_nr) ((3<<16) + (_nr))
- #define S3C2410_GPE(_nr) ((4<<16) + (_nr))
- #define S3C2410_GPF(_nr) ((5<<16) + (_nr))
- #define S3C2410_GPG(_nr) ((6<<16) + (_nr))
- #define S3C2410_GPH(_nr) ((7<<16) + (_nr))
- #define S3C2410_GPJ(_nr) ((8<<16) + (_nr))
- #define S3C2410_GPK(_nr) ((9<<16) + (_nr))
- #define S3C2410_GPL(_nr) ((10<<16) + (_nr))
- #define S3C2410_GPM(_nr) ((11<<16) + (_nr))
-
- /dts-v1/;
-
- / {
- model = "SMDK24440";
- compatible = "samsung,smdk2440";
-
- #address-cells = <1>;
- #size-cells = <1>;
-
- memory@30000000 {
- device_type = "memory";
- reg = <0x30000000 0x4000000>;
- };
- /*
- cpus {
- cpu {
- compatible = "arm,arm926ej-s";
- };
- };
- */
- chosen {
- bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
- };
-
-
- led {
- compatible = "jz2440_led";
- reg = <S3C2410_GPF(5) 1>;
- };
- };
-
设备树文件要放在内核的linux-4.19-rc3/arch/arm/boot/dts/ ,然后重新编译
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- #define S3C2440_GPA(n) (0<<16 | n)
- #define S3C2440_GPB(n) (1<<16 | n)
- #define S3C2440_GPC(n) (2<<16 | n)
- #define S3C2440_GPD(n) (3<<16 | n)
- #define S3C2440_GPE(n) (4<<16 | n)
- #define S3C2440_GPF(n) (5<<16 | n)
- #define S3C2440_GPG(n) (6<<16 | n)
- #define S3C2440_GPH(n) (7<<16 | n)
- #define S3C2440_GPI(n) (8<<16 | n)
- #define S3C2440_GPJ(n) (9<<16 | n)
-
- static int led_pin;
- static volatile unsigned int *gpio_con;
- static volatile unsigned int *gpio_dat;
-
- /* 123. 分配/设置/注册file_operations
- * 4. 入口
- * 5. 出口
- */
-
- static int major;
- static struct class *led_class;
-
- static unsigned int gpio_base[] = {
- 0x56000000, /* GPACON */
- 0x56000010, /* GPBCON */
- 0x56000020, /* GPCCON */
- 0x56000030, /* GPDCON */
- 0x56000040, /* GPECON */
- 0x56000050, /* GPFCON */
- 0x56000060, /* GPGCON */
- 0x56000070, /* GPHCON */
- 0, /* GPICON */
- 0x560000D0, /* GPJCON */
- };
-
- static int led_open (struct inode *node, struct file *filp)
- {
- /* 把LED引脚配置为输出引脚 */
- /* GPF5 - 0x56000050 */
- int bank = led_pin >> 16;
- int base = gpio_base[bank];
-
- int pin = led_pin & 0xffff;
- gpio_con = ioremap(base, 8);
- if (gpio_con) {
- printk("ioremap(0x%x) = 0x%x\n", base, gpio_con);
- }
- else {
- return -EINVAL;
- }
-
- gpio_dat = gpio_con + 1;
-
- *gpio_con &= ~(3<<(pin * 2));
- *gpio_con |= (1<<(pin * 2));
-
- return 0;
- }
-
- static ssize_t led_write (struct file *filp, const char __user *buf, size_t size, loff_t *off)
- {
- /* 根据APP传入的值来设置LED引脚 */
- unsigned char val;
- int pin = led_pin & 0xffff;
-
- copy_from_user(&val, buf, 1);
-
- if (val)
- {
- /* 点灯 */
- *gpio_dat &= ~(1<
- }
- else
- {
- /* 灭灯 */
- *gpio_dat |= (1<
- }
-
- return 1; /* 已写入1个数据 */
- }
-
- static int led_release (struct inode *node, struct file *filp)
- {
- printk("iounmap(0x%x)\n", gpio_con);
- iounmap(gpio_con);
- return 0;
- }
-
-
- static struct file_operations myled_oprs = {
- .owner = THIS_MODULE,
- .open = led_open,
- .write = led_write,
- .release = led_release,
- };
-
-
- static int led_probe(struct platform_device *pdev)
- {
- struct resource *res;
-
- /* 根据platform_device的资源进行ioremap */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- led_pin = res->start;
-
- major = register_chrdev(0, "myled", &myled_oprs);
-
- led_class = class_create(THIS_MODULE, "myled");
- device_create(led_class, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
-
- return 0;
- }
-
- static int led_remove(struct platform_device *pdev)
- {
- unregister_chrdev(major, "myled");
- device_destroy(led_class, MKDEV(major, 0));
- class_destroy(led_class);
-
- return 0;
- }
-
-
- static const struct of_device_id of_match_leds[] = {
- { .compatible = "jz2440_led", .data = NULL },
- { /* sentinel */ }
- };
-
-
- struct platform_driver led_drv = {
- .probe = led_probe,
- .remove = led_remove,
- .driver = {
- .name = "myled",
- .of_match_table = of_match_leds, /* 能支持哪些来自于dts的platform_device */
- }
- };
-
-
- static int myled_init(void)
- {
- platform_driver_register(&led_drv);
- return 0;
- }
-
- static void myled_exit(void)
- {
- platform_driver_unregister(&led_drv);
- }
-
-
-
- module_init(myled_init);
- module_exit(myled_exit);
-
-
- MODULE_LICENSE("GPL");
-
-
2.3 ledtest.c
-
- #include
- #include
- #include
- #include
-
- /* ledtest on
- * ledtest off
- */
- int main(int argc, char **argv)
- {
- int fd;
- unsigned char val = 1;
- fd = open("/dev/led", O_RDWR);
- if (fd < 0)
- {
- printf("can't open!\n");
- }
- if (argc != 2)
- {
- printf("Usage :\n");
- printf("%s
\n" , argv[0]); - return 0;
- }
-
- if (strcmp(argv[1], "on") == 0)
- {
- val = 1;
- }
- else
- {
- val = 0;
- }
-
- write(fd, &val, 1);
- return 0;
- }
2.4 Makefile
- KERN_DIR = /work/system/linux-4.19-rc3
-
- all:
- make -C $(KERN_DIR) M=`pwd` modules
-
- clean:
- make -C $(KERN_DIR) M=`pwd` modules clean
- rm -rf modules.order
-
- obj-m += led_drv.o
-
3 代码优化改进
3.1 设备树文件
- // SPDX-License-Identifier: GPL-2.0
- /*
- * SAMSUNG SMDK2440 board device tree source
- *
- * Copyright (c) 2018 weidongshan@qq.com
- * dtc -I dtb -O dts -o jz2440.dts jz2440.dtb
- */
-
- #define S3C2410_GPA(_nr) ((0<<16) + (_nr))
- #define S3C2410_GPB(_nr) ((1<<16) + (_nr))
- #define S3C2410_GPC(_nr) ((2<<16) + (_nr))
- #define S3C2410_GPD(_nr) ((3<<16) + (_nr))
- #define S3C2410_GPE(_nr) ((4<<16) + (_nr))
- #define S3C2410_GPF(_nr) ((5<<16) + (_nr))
- #define S3C2410_GPG(_nr) ((6<<16) + (_nr))
- #define S3C2410_GPH(_nr) ((7<<16) + (_nr))
- #define S3C2410_GPJ(_nr) ((8<<16) + (_nr))
- #define S3C2410_GPK(_nr) ((9<<16) + (_nr))
- #define S3C2410_GPL(_nr) ((10<<16) + (_nr))
- #define S3C2410_GPM(_nr) ((11<<16) + (_nr))
-
- /dts-v1/;
-
- / {
- model = "SMDK24440";
- compatible = "samsung,smdk2440";
-
- #address-cells = <1>;
- #size-cells = <1>;
-
- memory@30000000 {
- device_type = "memory";
- reg = <0x30000000 0x4000000>;
- };
- /*
- cpus {
- cpu {
- compatible = "arm,arm926ej-s";
- };
- };
- */
- chosen {
- bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
- };
-
-
- led {
- compatible = "jz2440_led";
- pin = <S3C2410_GPF(5)>;
- };
- };
-
3.2 led_drv.c
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- #define S3C2440_GPA(n) (0<<16 | n)
- #define S3C2440_GPB(n) (1<<16 | n)
- #define S3C2440_GPC(n) (2<<16 | n)
- #define S3C2440_GPD(n) (3<<16 | n)
- #define S3C2440_GPE(n) (4<<16 | n)
- #define S3C2440_GPF(n) (5<<16 | n)
- #define S3C2440_GPG(n) (6<<16 | n)
- #define S3C2440_GPH(n) (7<<16 | n)
- #define S3C2440_GPI(n) (8<<16 | n)
- #define S3C2440_GPJ(n) (9<<16 | n)
-
- static int led_pin;
- static volatile unsigned int *gpio_con;
- static volatile unsigned int *gpio_dat;
-
- /* 123. 分配/设置/注册file_operations
- * 4. 入口
- * 5. 出口
- */
-
- static int major;
- static struct class *led_class;
-
- static unsigned int gpio_base[] = {
- 0x56000000, /* GPACON */
- 0x56000010, /* GPBCON */
- 0x56000020, /* GPCCON */
- 0x56000030, /* GPDCON */
- 0x56000040, /* GPECON */
- 0x56000050, /* GPFCON */
- 0x56000060, /* GPGCON */
- 0x56000070, /* GPHCON */
- 0, /* GPICON */
- 0x560000D0, /* GPJCON */
- };
-
- static int led_open (struct inode *node, struct file *filp)
- {
- /* 把LED引脚配置为输出引脚 */
- /* GPF5 - 0x56000050 */
- int bank = led_pin >> 16;
- int base = gpio_base[bank];
-
- int pin = led_pin & 0xffff;
- gpio_con = ioremap(base, 8);
- if (gpio_con) {
- printk("ioremap(0x%x) = 0x%x\n", base, gpio_con);
- }
- else {
- return -EINVAL;
- }
-
- gpio_dat = gpio_con + 1;
-
- *gpio_con &= ~(3<<(pin * 2));
- *gpio_con |= (1<<(pin * 2));
-
- return 0;
- }
-
- static ssize_t led_write (struct file *filp, const char __user *buf, size_t size, loff_t *off)
- {
- /* 根据APP传入的值来设置LED引脚 */
- unsigned char val;
- int pin = led_pin & 0xffff;
-
- copy_from_user(&val, buf, 1);
-
- if (val)
- {
- /* 点灯 */
- *gpio_dat &= ~(1<
- }
- else
- {
- /* 灭灯 */
- *gpio_dat |= (1<
- }
-
- return 1; /* 已写入1个数据 */
- }
-
- static int led_release (struct inode *node, struct file *filp)
- {
- printk("iounmap(0x%x)\n", gpio_con);
- iounmap(gpio_con);
- return 0;
- }
-
-
- static struct file_operations myled_oprs = {
- .owner = THIS_MODULE,
- .open = led_open,
- .write = led_write,
- .release = led_release,
- };
-
-
- static int led_probe(struct platform_device *pdev)
- {
- struct resource *res;
-
- /* 根据platform_device的资源进行ioremap */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res) {
- led_pin = res->start;
- }
- else {
- /* 获得pin属性 */
- of_property_read_s32(pdev->dev.of_node, "pin", &led_pin);
- }
-
- if (!led_pin)
- {
- printk("can not get pin for led\n");
- return -EINVAL;
- }
-
-
- major = register_chrdev(0, "myled", &myled_oprs);
-
- led_class = class_create(THIS_MODULE, "myled");
- device_create(led_class, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
-
- return 0;
- }
-
- static int led_remove(struct platform_device *pdev)
- {
- unregister_chrdev(major, "myled");
- device_destroy(led_class, MKDEV(major, 0));
- class_destroy(led_class);
-
- return 0;
- }
-
-
- static const struct of_device_id of_match_leds[] = {
- { .compatible = "jz2440_led", .data = NULL },
- { /* sentinel */ }
- };
-
-
- struct platform_driver led_drv = {
- .probe = led_probe,
- .remove = led_remove,
- .driver = {
- .name = "myled",
- .of_match_table = of_match_leds, /* 能支持哪些来自于dts的platform_device */
- }
- };
-
-
- static int myled_init(void)
- {
- platform_driver_register(&led_drv);
- return 0;
- }
-
- static void myled_exit(void)
- {
- platform_driver_unregister(&led_drv);
- }
-
-
-
- module_init(myled_init);
- module_exit(myled_exit);
-
-
- MODULE_LICENSE("GPL");
-
-
3.3 ledtest.c
-
- #include
- #include
- #include
- #include
-
- /* ledtest on
- * ledtest off
- */
- int main(int argc, char **argv)
- {
- int fd;
- unsigned char val = 1;
- fd = open("/dev/led", O_RDWR);
- if (fd < 0)
- {
- printf("can't open!\n");
- }
- if (argc != 2)
- {
- printf("Usage :\n");
- printf("%s
\n" , argv[0]); - return 0;
- }
-
- if (strcmp(argv[1], "on") == 0)
- {
- val = 1;
- }
- else
- {
- val = 0;
- }
-
- write(fd, &val, 1);
- return 0;
- }
3.4 Makefile
- KERN_DIR = /work/system/linux-4.19-rc3
-
- all:
- make -C $(KERN_DIR) M=`pwd` modules
-
- clean:
- make -C $(KERN_DIR) M=`pwd` modules clean
- rm -rf modules.order
-
- obj-m += led_drv.o
-
4 实验方式
4.1 编译设备树文件jz2440.dts得到jz2440.dtb
假设内核目录为: /work/system/linux-4.19-rc3/
设置PATH使用新工具链:
export PATH=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/work/system/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabi/bin
把jz2440.dts复制到内核目录:
cp jz2440.dts /work/system/linux-4.19-rc3/arch/arm/boot/dts
编译设备树:
- cd /work/system/linux-4.19-rc3/
- make dtbs
得到 /work/system/linux-4.19-rc3/arch/arm/boot/dts/jz2440.dtb,把dtb文件复制到nfs目录, 假设为 /work/nfs_root。
4.2 使用jz2440.dtb启动内核
启动板子进入u-boot :
从NFS下载dtb:
nfs 32000000 192.168.1.123:/work/nfs_root/jz2440.dtb
烧写到nand flash:
nand erase device_tree; nand write.jffs2 32000000 device_tree
重启单板
然后在/sys/devices/platform目录下就可以看到
然后再进入5005.led/of_node
前面的00050005对应的是设备树文件里面的寄存器值,后面的00000001对应的是设备树文件中寄存器的大小也就是1.
4.3 编译led_drv.c
设置PATH使用新工具链:
- export PATH=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/work/system/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabi/bin
- cd 001th/driver 或 cd 002th/driver
- make // 得到led_drv.ko
4.4 编译app ledtest
注意!注意!注意!注意!注意!注意!注意!注意!编译APP用的工具链不一样!!!!为什么编译驱动程序的工具链和编译app的工具链不一样,原因见后面附录5.
设置PATH使用新工具链:
- export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/local/arm/4.3.2/bin
- arm-linux-gcc -o ledtest ledtest.c
4.5 测试
在单板上通过nfs得到led_drv.ko和ledtest,测试命令:
- insmod led_drv.ko
- ./ledtest on
- ./ledtest off
5 附录
- 1. 编译器的选择:
- 一个完整的Linux系统包含三部分: u-boot, kernel, root filesystem.
- a. 对于u-boot:
- 我们仍然使用u-boot 1.1.6, 在这个版本上我们实现了很多功能: usb下载,菜单操作,网卡永远使能等, 不忍丢弃.
-
- b. 对于kernel:
- 我下载了目前(2018.09.19)最新的内核 (4.19)
-
- c. 对于root filesystem
- 中文名为"根文件系统", 它包含一些必须的APP, 一些动态库。
- 一般来说这些动态库是从工具链里的lib目录复制得到的,
- 当然也可以自己去编译glibc等库。
-
- 在编译u-boot和kernel时, 我们可以使用新的工具链,
- 只要这个工具链支持ARM9的指令集(armv4)就可以(这通常可以通过编译参数来指定使用特定的指令集).
- 工具链可以从某些网站上下载,并不需要自己去制作。
- 比如可以访问这个网站: https://releases.linaro.org/components/toolchain/binaries/4.9-2017.01/arm-linux-gnueabi/
- 下载: gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabi.tar.xz
-
- 但是在制作根文件系统时, 实际上我们是编译各种APP,
- 这些APP要用到一些动态库, 为了方便一般直接使用工具链中lib目录里的库。
- 新版工具链的lib库一般是支持新的芯片,比如cortex A7,A8,A9,并不支持ARM9。
- 所以在制作根文件系统、编译APP时我们还得使用比较老的工具链: arm-linux-gcc-4.3.2.tar.bz2
-
-
- 2. 通过设置PATH环境变量来选择使用某个工具链:
- 2.1 安装工具链:
- 这非常简单, 解压即可:
- sudo tar xjf arm-linux-gcc-4.3.2.tar.bz2 -C / (解压到根目录, /usr/local/arm/4.3.2/bin/下就是工具链)
- tar xJf gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabi.tar.xz (解压到当前目录, 假设/work/system/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabi/bin下就是工具链)
-
- 注意: "/work/system" 请自行替换为你的实际目录
-
- 2.2 设置环境变量使用某个工具链:
- a. 要使用arm-linux-gcc 4.3.2, 执行如下命令:
- export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/local/arm/4.3.2/bin
- 然后就可以执行 arm-linux-gcc -v 观看到版本号了
-
- b. 要使用arm-linux-gnueabi-gcc 4.9.4, 执行如下命令:
- export PATH=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/work/system/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabi/bin
- 然后就可以执行 arm-linux-gnueabi-gcc -v 观看到版本号了
-
-
-
- 2. u-boot的编译:
- a. 首先设置环境变量使用要使用arm-linux-gnueabi-gcc 4.3.2
- b.
- 把源文件u-boot-1.1.6.tar.bz2、补丁文件u-boot-1.1.6_device_tree_for_jz2440.patch放在同一个目录,
- 执行如下命令:
- tar xjf u-boot-1.1.6.tar.bz2 // 解压
- cd u-boot-1.1.6
- patch -p1 < ../u-boot-1.1.6_device_tree_for_jz2440.patch // 打补丁
- make 100ask24x0_config // 配置
- make // 编译, 可以得到u-boot.bin
-
- 3. kernel的编译:
- a. 首先设置环境变量使用要使用arm-linux-gnueabi-gcc 4.3.2
- b.
- 把源文件linux-4.19-rc3.tar.gz、补丁文件linux-4.19-rc3_device_tree_for_jz2440.patch放在同一个目录,
- 执行如下命令:
- tar xzf linux-4.19-rc3.tar.gz // 解压
- cd linux-4.19-rc3
- patch -p1 < ../linux-4.19-rc3_device_tree_for_jz2440.patch // 打补丁
- cp config_ok .config // 配置
- make uImage // 编译, 可以得到arch/arm/boot/uImage
- make dtbs // 编译, 可以得到arch/arm/boot/dts/jz2440.dtb
-
- 注意:
- a. 如果提示"mkimage not found", 先编译u-boot, 把tools/mkimage复制到/bin目录
- b. 如果提示"openssl/bio.h: No such file or directory"
- 先确保你的ubuntu可以上网, 然后执行如下命令:
- sudo apt-get update
- sudo apt-get install libssl-dev
-
- 4. 制作root filesystem :
- 可以直接使用映象文件: fs_mini_mdev_new.yaffs2
-
- 如果想自己制作,请参考视频:
- 从www.100ask.net下载页面打开百度网盘,
- 打开如下目录:
- 100ask分享的所有文件
- 009_UBOOT移植_LINUX移植_驱动移植(免费)
- 毕业班第3课_移植3.4.2内核
- 毕业班第3课第2节_移植3.4.2内核之修改分区及制作根文件系统.WMV
-
-
-
- 5. 烧写
- a. 使用EOP烧写u-boot.bin到JZ2440的nor flash或nand flash
- b. 启动u-boot, 在串口工具中输入相应菜单命令, 使用dnw_100ask.exe发送对应文件
-
- 菜单 要发送的文件
- [k] Download Linux kernel uImage uImage
- [t] Download device tree file(.dtb) jz2440.dtb
- [y] Download root_yaffs image fs_mini_mdev_new.yaffs2
-
- 烧写完毕即可重启进入板上LINUX系统。
-
-
-
-
-
-
相关阅读:
申请CNAS软件测试资质,如何选择测试工具最具性价比?
当我们在谈论构造函数注入的时候我们在谈论什么
C++之STL前序
坑惨啦!!!——符号冲突案例分析
Vue3.3指北(一)
制作游戏demo的心得
Nginx可用参数
Windows上搭建一个网站(基本生产环境)
【无标题】【Koltin Flow(三)】Flow操作符之中间操作符(二)
rush learn note
-
原文地址:https://blog.csdn.net/u013171226/article/details/126708917