日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

linux内核led驱动开发,从Linux内核LED驱动来理解字符设备驱动开发流程

發(fā)布時(shí)間:2023/12/8 linux 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux内核led驱动开发,从Linux内核LED驱动来理解字符设备驱动开发流程 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄

博客說(shuō)明

開發(fā)環(huán)境

1. Linux字符設(shè)備驅(qū)動(dòng)的組成

1.1 字符設(shè)備驅(qū)動(dòng)模塊加載與卸載函數(shù)

1.2 字符設(shè)備驅(qū)動(dòng)的file_operations 結(jié)構(gòu)體中的成員函數(shù)

2. 字符設(shè)備驅(qū)動(dòng)——設(shè)備號(hào)注冊(cè)卸載

2.1 設(shè)備號(hào)注冊(cè)

2.2 設(shè)備號(hào)注銷

3. 字符設(shè)備驅(qū)動(dòng)——文件操作

參考資料

示例代碼

@(從Linux內(nèi)核LED驅(qū)動(dòng)來(lái)理解字符設(shè)備驅(qū)動(dòng)開發(fā)流程)

博客說(shuō)明

撰寫日期

2018.12.08

完稿日期

2019.10.06

最近維護(hù)

暫無(wú)

本文作者

multimicro

聯(lián)系方式

[email?protected]

GitHub

https://github.com/wifialan

本文地址

https://blog.csdn.net/multimicro/article/details/84898135

開發(fā)環(huán)境

環(huán)境說(shuō)明

詳細(xì)信息

備注信息

操作系統(tǒng)

Ubunut 18.04.3 LTS

開發(fā)板

S3C2440(JZ2440-V3)

kernel版本

Linux-3.4.2

官網(wǎng)地址

busybox版本

busybox-1.22.1

官網(wǎng)地址

編譯器

arm-Linux-gcc-4.4.3

下載地址

編譯器路徑

/opt/FriendlyARM/toolschain/4.4.3/bin

絕對(duì)路徑

1. Linux字符設(shè)備驅(qū)動(dòng)的組成

引自宋寶華《Linux設(shè)備驅(qū)動(dòng)開發(fā)詳解--基于最新的Linux 4.0內(nèi)核》P138內(nèi)容:

在Linux中,字符設(shè)備驅(qū)動(dòng)由如下幾個(gè)部分組成。

1. 字符設(shè)備驅(qū)動(dòng)模塊加載與卸載函數(shù)

2. 字符設(shè)備驅(qū)動(dòng)的file_operations 結(jié)構(gòu)體中的成員函數(shù)

這里先介紹一下字符設(shè)備的開發(fā)流程:字符設(shè)備驅(qū)動(dòng)是通過(guò)設(shè)備號(hào) 與上位機(jī)程序連接。而上位機(jī)程序?qū)︱?qū)動(dòng)的控制則是通過(guò)文件操作,即read、write、ioctl等完成。

ps.(對(duì)于linux系統(tǒng)而言,一切皆文件,驅(qū)動(dòng)加載成功后,會(huì)在/proc/devices里面添加驅(qū)動(dòng)節(jié)點(diǎn)號(hào)信息)

因此一個(gè)字符設(shè)備驅(qū)動(dòng)應(yīng)包含1. 設(shè)備號(hào)的注冊(cè)、卸載和2. 文件操作兩個(gè)功能,注冊(cè)的設(shè)備號(hào)用于提供接口,而文件操作用于對(duì)驅(qū)動(dòng)的操作。

字符設(shè)備驅(qū)動(dòng)的結(jié)構(gòu)如下圖所示:

對(duì)于cdev_init函數(shù)中,建立file_operations之間的連接的疑問(wèn),看一下cdev_init的實(shí)現(xiàn)

void cdev_init(struct cdev *cdev, const struct file_operations *fops)

{

memset(cdev, 0, sizeof *cdev);

INIT_LIST_HEAD(&cdev->list);

kobject_init(&cdev->kobj, &ktype_cdev_default);

cdev->ops = fops;

}

可以看出,最后的一個(gè)語(yǔ)句cdev->ops = fops;完成了在cdev中的file_operations的綁定

下面從程序語(yǔ)言角度感性的認(rèn)識(shí)一下設(shè)備號(hào)的注冊(cè)、卸載函數(shù)原型,和文件操作函數(shù)原型。

1.1 字符設(shè)備驅(qū)動(dòng)模塊加載與卸載函數(shù)

//加載函數(shù)

static int __init xxx_init(void)

{

... ...

}

//卸載函數(shù)

static int __exit xxx_exit(void)

{

... ...

}

1.2 字符設(shè)備驅(qū)動(dòng)的file_operations 結(jié)構(gòu)體中的成員函數(shù)

static const struct file_operations xxx_fileops = {

.owner = THIS_MODULE,

.write = xxx_write,

.read = xxx_read,

.open = xxx_open,

.unlocked_ioctl = xxx_ioctl,

... ...

};

static int xxx_open( struct inode *inodes, struct file *filp )

{

... ...

}

static long xxx_ioctl( struct file *file, unsigned int cmd, unsigned long arg )

{

... ...

}

static ssize_t xxx_write( struct file *filp, const char __user *buffer, size_t size, loff_t *f_pos )

{

... ...

}

static ssize_t xxx_read( struct file *filp, const char __user *buffer, size_t size, loff_t *f_pos )

{

... ...

}

2. 字符設(shè)備驅(qū)動(dòng)——設(shè)備號(hào)注冊(cè)卸載

以我寫的字符設(shè)備驅(qū)動(dòng)源代碼為例,路徑為L(zhǎng)inux-3.4.2\drivers\char\s3c2440_leds.c,文章后附有完整的代碼

設(shè)備號(hào)的注冊(cè)由static int __init s3c2440_leds_init(void)完成

設(shè)備號(hào)的卸載由static int __init s3c2440_leds_exit(void)完成

首先分析設(shè)備號(hào)的注冊(cè),然后分析卸載

2.1 設(shè)備號(hào)注冊(cè)

設(shè)備號(hào)分為主設(shè)備號(hào)和次設(shè)備號(hào),若源代碼中定義了主設(shè)備號(hào)(次設(shè)備號(hào)一般為0),那么可以直接完成設(shè)備號(hào)的注冊(cè),其流程為

注冊(cè)成功后,可通過(guò)cat /proc/devices命令查看設(shè)備號(hào)

2.2 設(shè)備號(hào)注銷

相比設(shè)備號(hào)的注冊(cè),注銷流程就十分簡(jiǎn)單:

3. 字符設(shè)備驅(qū)動(dòng)——文件操作

當(dāng)上位機(jī)程序通過(guò)調(diào)用open函數(shù)打開(鏈接上)相應(yīng)的驅(qū)動(dòng)程序后,open函數(shù)會(huì)返回一個(gè)==文件描述符==暫且記為fd,然后對(duì)該驅(qū)動(dòng)的read、write、ioctl等操作都可以通過(guò)使用fd完成。簡(jiǎn)單的字符設(shè)備驅(qū)動(dòng)程序大多采用ioctl函數(shù)控制驅(qū)動(dòng)程序,而這個(gè)ioctl函數(shù)本身也不難,其實(shí)現(xiàn)為:

static long s3c2440_leds_ioctl( struct file *file, unsigned int cmd, unsigned long arg )

函數(shù)中

第一個(gè)參數(shù):表示要操作的文件描述符

第二個(gè)參數(shù):表示傳遞的命令字

第三個(gè)參數(shù):表示傳遞的變量字

第二個(gè)參數(shù)和第三個(gè)參數(shù)的含義沒(méi)有硬性規(guī)定,傳遞的參數(shù)符合對(duì)應(yīng)的關(guān)鍵字限定類型即可

下面的給出示例參考

static long s3c2440_leds_ioctl( struct file *file, unsigned int cmd, unsigned long arg )

{

printk(DRV_NAME "\tRecv cmd: %u\n", cmd);

printk(DRV_NAME "\tRecv arg: %lu\n", arg);

//IO operations function.

if(arg > 4) {

return -EINVAL;

}

switch (cmd) {

case IOCTL_LED_ON: //#define IOCTL_LED_ON 1

s3c2410_gpio_setpin(S3C2410_GPF(arg+3), 0);//Set pin

printk("Open LED %lu ",arg);

return 0;

case IOCTL_LED_OFF: //#define IOCTL_LED_OFF 0

s3c2410_gpio_setpin(S3C2410_GPF(arg+3), 1);

printk("Close LED %lu ",arg);

return 0;

default:

return -EINVAL;

}

}

參考資料

1. 宋寶華《Linux設(shè)備驅(qū)動(dòng)開發(fā)詳解–基于最新的Linux 4.0內(nèi)核》 第6章 字符設(shè)備驅(qū)動(dòng)

示例代碼

/*

* Driver for S3C2440 base board.

*

* Copyright (C) 2019 Alan NWPU

*

* This program is free software; you can redistribute it and/or modify

* it under the terms of the GNU General Public License as published by

* the Free Software Foundation; either version 2 of the License, or

* (at your option) any later version.

*

* This program is distributed in the hope that it will be useful,

* but WITHOUT ANY WARRANTY; without even the implied warranty of

* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

* GNU General Public License for more details.

*

* You should have received a copy of the GNU General Public License

* along with this program; if not, write to the Free Software

* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

* MULTIBEANS, NPU Youyi West Ave, Beilin District, Xi'an, China.

*/

#include /* Every Linux kernel module must include this head */

#include /* Every Linux kernel module must include this head */

#include /* printk() */

#include /* struct fops */

#include /* error codes */

#include /* cdev_alloc() */

#include /* request_mem_region() */

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define DRV_NAME "s3c2440_leds"

#define DRV_AUTHOR "Alan Tian "

#define DRV_DESC "S3C2440 LED Pin Driver"

#define S3C2440_LED_SIZE 0x1000

#define S3C2440_LED_MAJOR 230

#define S3C2440_LED_MINOR 0

static int major = S3C2440_LED_MAJOR;

static int minor = S3C2440_LED_MINOR;

/* 應(yīng)用程序執(zhí)行ioctl(fd, cmd, arg)時(shí)的第2個(gè)參數(shù) */

#define IOCTL_LED_ON 0

#define IOCTL_LED_OFF 1

static int s3c2440_leds_open( struct inode *inodes, struct file *filp );

static long s3c2440_leds_ioctl( struct file *file, unsigned int cmd, unsigned long arg );

static ssize_t s3c2440_leds_write( struct file *filp, const char __user *buffer, size_t size, loff_t *f_pos );

static ssize_t s3c2440_leds_read( struct file *filp, const char __user *buffer, size_t size, loff_t *f_pos );

struct s3c2440_leds_dev_t

{

struct cdev cdev;

unsigned char mem[S3C2440_LED_SIZE];

} *s3c2440_leds_dev;

//Step 2: Add file operations

static const struct file_operations s3c2440_leds_fileops = {

.owner = THIS_MODULE,

.write = s3c2440_leds_write,

.read = s3c2440_leds_read,

.open = s3c2440_leds_open,

.unlocked_ioctl = s3c2440_leds_ioctl,

};

static int s3c2440_leds_open( struct inode *inodes, struct file *filp )

{

//int ret;

filp->private_data = s3c2440_leds_dev;

printk(DRV_NAME"\tS3C2440 open function...\n");

return 0;

}

static long s3c2440_leds_ioctl( struct file *file, unsigned int cmd, unsigned long arg )

{

printk(DRV_NAME "\tRecv cmd: %u\n", cmd);

printk(DRV_NAME "\tRecv arg: %lu\n", arg);

//IO operations function.

if(arg > 4) {

return -EINVAL;

}

switch (cmd) {

case IOCTL_LED_ON:

s3c2410_gpio_setpin(S3C2410_GPF(arg+3), 0);//Set pin

printk("Open LED %lu ",arg);

return 0;

case IOCTL_LED_OFF:

s3c2410_gpio_setpin(S3C2410_GPF(arg+3), 1);

printk("Close LED %lu ",arg);

return 0;

default:

return -EINVAL;

}

}

static ssize_t s3c2440_leds_write( struct file *filp, const char __user *buffer, size_t size, loff_t *f_pos )

{

unsigned long p = *f_pos;

unsigned int count = size;

int ret = 0;

struct s3c2440_leds_dev_t *dev = filp->private_data;

if(p >= S3C2440_LED_SIZE)

return 0;

if(count > S3C2440_LED_SIZE - p)

count = S3C2440_LED_SIZE - p;

memset(dev->mem, 0, S3C2440_LED_SIZE);

if(copy_from_user(dev->mem + p, buffer, count)) {

ret = -EFAULT;

} else {

*f_pos += count;

ret = count;

printk(KERN_INFO "writter %u bytes(s) from %lu\n", count, p);

}

return ret;

}

static ssize_t s3c2440_leds_read( struct file *filp, const char __user *buffer, size_t size, loff_t *f_pos )

{

unsigned long p = *f_pos;

unsigned int count = size;

int ret = 0;

struct s3c2440_leds_dev_t *dev = filp->private_data;

if(p >= S3C2440_LED_SIZE)

return 0;

if(count > S3C2440_LED_SIZE - p)

count = S3C2440_LED_SIZE - p;

if(copy_to_user(buffer, dev->mem + p, count)) {

ret = -EFAULT;

} else {

*f_pos += count;

ret = count;

printk(KERN_INFO "read %u bytes(s) from %lu\n", count, p);

}

return ret;

}

static int __init s3c2440_leds_init(void)

{

int ret,err;

dev_t devid;

if(major) {

devid = MKDEV(major, 0);

ret = register_chrdev_region(devid, 1, DRV_NAME);

printk("Origin Creat node %d\n",major);

} else {

ret = alloc_chrdev_region(&devid, 0, 1, DRV_NAME);

major = MAJOR(devid);

printk("Arrage1 Creat node %d\n",major);

}

if(ret < 0) {

printk(DRV_NAME "\ts3c2440 new device failed\n");

//goto fail_malloc;

return ret;

}

s3c2440_leds_dev = kzalloc(sizeof(struct s3c2440_leds_dev_t), GFP_KERNEL);

if(!s3c2440_leds_dev) {

ret = -ENOMEM;

goto fail_malloc;

}

printk("success init leds\n");

cdev_init(&s3c2440_leds_dev->cdev, &s3c2440_leds_fileops);

err = cdev_add(&s3c2440_leds_dev->cdev, devid, 1);

if(err)

printk(KERN_NOTICE "Error %d adding s2c2440_leds %d",err, 1);

return 0;

fail_malloc:

unregister_chrdev_region(devid, 1);

return ret;

}

static void __exit s3c2440_leds_exit(void)

{

printk("Starting delet node %d\n",major);

cdev_del(&s3c2440_leds_dev->cdev);

kfree(s3c2440_leds_dev);

unregister_chrdev_region(MKDEV(major, minor), 1);

printk("Delete node %d\n",major);

}

module_init(s3c2440_leds_init);

module_exit(s3c2440_leds_exit);

MODULE_AUTHOR(DRV_AUTHOR);

MODULE_DESCRIPTION(DRV_DESC);

MODULE_LICENSE("GPL");

總結(jié)

以上是生活随笔為你收集整理的linux内核led驱动开发,从Linux内核LED驱动来理解字符设备驱动开发流程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。