Android字符设备驱动开发基于高通msm8916【原创 】

本人才疏浅学,写一篇文档总结自己在msm8916平台上移植自己编写的简单的字符设备驱动开发的整个流程。这个小项目的主要功能是开发一个简单的APP,APP通过JNI去调用位于kernel的字符设备驱动。

APP的设计,开发平台Android Studio

主要的文件是下面的三个文件:

MainActivity.java文件的内容如下:

 1 package com.example.administrator.myled;
 2
 3 import android.nfc.Tag;
 4 import android.support.v7.app.AppCompatActivity;
 5 import android.os.Bundle;
 6 import android.util.Log;
 7 import android.view.View;
 8 import android.widget.Button;
 9 import android.widget.Toast;
10
11
12 import com.zbahuang.led.lowlevel.LedNative;
13
14 public class MainActivity extends AppCompatActivity implements View.OnClickListener {
15     private final static String TAG = "zbzhuang";
16     Button btn_led_on;
17     Button btn_led_off;
18     LedNative myled;
19
20
21     @Override
22     protected void onCreate(Bundle savedInstanceState) {
23         super.onCreate(savedInstanceState);
24         setContentView(R.layout.activity_main);
25
26         initUI();
27
28         myled = new LedNative();
29         myled.openDev();
30         Log.d(TAG,"app:open Dev");
31     }
32
33     private void initUI() {
34         btn_led_on = (Button) findViewById(R.id.btn_led_on);
35         btn_led_on.setOnClickListener(this);
36
37         btn_led_off = (Button) findViewById(R.id.btn_led_off);
38         btn_led_off.setOnClickListener(this);
39     }
40
41     @Override
42     public void onClick(View v) {
43         switch (v.getId()){
44             case R.id.btn_led_on:
45                 Toast.makeText(MainActivity.this,"拉灯-->",Toast.LENGTH_SHORT).show();
46                 Log.d(TAG,"app:LED on");
47                 myled.devOn();
48                 break;
49             case R.id.btn_led_off:
50                 Toast.makeText(MainActivity.this,"灭灯-->",Toast.LENGTH_SHORT).show();
51                 Log.d(TAG,"app:LED off");
52                 myled.devOff();
53                 break;
54             default:
55                 break;
56         }
57
58     }
59
60     @Override
61     protected void onDestroy() {
62         super.onDestroy();
63         myled.closeDev();
64         Log.d(TAG,"app:close Dev");
65     }
66 }
LedNative.java文件的内容如下:在这个文件中所声明的方法是在jni中实现的啊。使用了特殊的关键字表示该方法是在JNI当中实现的啊。
 1 package com.zbahuang.led.lowlevel;
 2
 3 /**
 4  * Created by Administrator on 2017/3/29 0029.
 5  */
 6
 7 public class LedNative {
 8
 9     static {
10         System.loadLibrary("led_jni");
11     }
12
13     public native int openDev();
14     public native int devOn();
15     public native int devOff();
16     public native int closeDev();
17 }
18 package com.zbahuang.led.lowlevel;
19
20 /**
21  * Created by Administrator on 2017/3/29 0029.
22  */
23
24 public class LedNative {
25
26     static {
27         System.loadLibrary("led_jni");
28     }
29
30     public native int openDev();
31     public native int devOn();
32     public native int devOff();
33     public native int closeDev();
34 }

activity_main.xml文件的内容如下:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     xmlns:app="http://schemas.android.com/apk/res-auto"
 4     xmlns:tools="http://schemas.android.com/tools"
 5     android:layout_width="match_parent"
 6     android:layout_height="match_parent"
 7     tools:context="com.example.administrator.myled.MainActivity">
 8
 9
10     <RelativeLayout
11         android:layout_width="394dp"
12         android:layout_height="520dp"
13         tools:layout_editor_absoluteX="-5dp"
14         tools:layout_editor_absoluteY="-10dp">
15
16         <Button
17             android:id="@+id/btn_led_on"
18             android:layout_width="wrap_content"
19             android:layout_height="wrap_content"
20             android:layout_alignParentStart="true"
21             android:layout_alignParentTop="true"
22             android:layout_marginStart="56dp"
23             android:layout_marginTop="109dp"
24             android:text="拉灯" />
25
26         <Button
27             android:id="@+id/btn_led_off"
28             android:layout_width="wrap_content"
29             android:layout_height="wrap_content"
30             android:layout_alignBaseline="@+id/btn_led_on"
31             android:layout_alignBottom="@+id/btn_led_on"
32             android:layout_marginStart="81dp"
33             android:layout_toEndOf="@+id/btn_led_on"
34             android:text="灭灯" />
35     </RelativeLayout>
36 </android.support.constraint.ConstraintLayout>

JNI的文件:

led_jni.cpp

  1 /*1. 头文件*/
  2 #include<linux/init.h>
  3 #include<linux/module.h>
  4 #include<linux/fs.h>
  5 #include<linux/device.h>
  6 #include<linux/slab.h>
  7 #include<linux/cdev.h>
  8 #include<asm/uaccess.h>
  9 #include<asm/io.h>
 10
 11 static unsigned int led_major=0;
 12 volatile unsigned long *gpc0con = NULL;
 13 volatile unsigned long *gpc0dat = NULL;
 14
 15 struct led_device{
 16     struct class *led_class ;    //表示一类设备, 存储某些信息
 17     struct device *led_device ;    //表示一个设备
 18     struct cdev  *led_cdev;
 19     unsigned int val;
 20
 21 };
 22
 23 struct led_device *s5pv_led_dev;
 24
 25
 26 /*可用于查询LED的状态*/
 27 static ssize_t led_read(struct file *file, char __user *buf, size_t count, loff_t *opps)
 28 {
 29     int ret;
 30
 31     ret = copy_to_user(buf, &s5pv_led_dev->val, count);
 32     if(ret>0)
 33     {
 34         printk(KERN_ERR "zbzhuang### copy to user failed!\n");
 35         return ret;
 36     }
 37
 38     printk(KERN_INFO "zbzhuang### val=%d\n", s5pv_led_dev->val);
 39
 40     return ret?0:count;
 41 }
 42 static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *opps)
 43 {
 44     int ret;
 45
 46     /*拷贝成功,返回0; 拷贝失败, 返回没有被拷贝的字节数*/
 47     ret = copy_from_user(&s5pv_led_dev->val, buf, count);
 48     if(ret>0)
 49     {
 50         printk(KERN_ERR "zbzhuang### copy from user failed!\n");
 51         return ret;
 52     }
 53
 54     if(s5pv_led_dev->val)
 55     {
 56         /*点亮LED*/
 57         //*gpc0dat |= ((0x1<<3)|(0x1<<4));
 58         printk(KERN_ERR "zbzhuang### led on\n");
 59     }
 60     else
 61     {
 62         /*熄灭LED*/
 63 //        *gpc0dat &= ~((0x1<<3)|(0x1<<4));
 64         printk(KERN_ERR "zbzhuang### led off\n");
 65     }
 66
 67     return ret?0:count;
 68 }
 69
 70
 71 static int led_open(struct inode *inode, struct file *file)
 72 {
 73 #if 0
 74     /*1. 将物理地址映射为虚拟地址*/
 75     gpc0con = ioremap(0xE0200060, 8);
 76     gpc0dat = gpc0con +1;
 77
 78     /*2. 初始化GPC0_3,4引脚功能为输出*/
 79     *gpc0con &= ~((0xf<<12)|(0xf<<16));
 80     *gpc0con |=  ((0x1<<12)|(0x1<<16));
 81 #endif
 82     printk(KERN_ERR "zbzhuang### -----------%s-------------\n",__FUNCTION__);
 83
 84     return 0;
 85 }
 86
 87 static int led_close(struct inode *inode, struct file *file)
 88 {
 89 #if 0
 90
 91     *gpc0con &= ~((0xf<<12)|(0xf<<16));
 92     iounmap(gpc0con);
 93 #endif
 94     printk(KERN_ERR "zbzhuang### ------------%s-----------------\n",__FUNCTION__);
 95
 96     return 0;
 97
 98 }
 99
100 /*硬件操作方法*/
101 struct file_operations led_fops={
102     .owner = THIS_MODULE,    //当前模块所用
103     .open  = led_open,
104     .write = led_write,
105     .read  = led_read,
106     .release = led_close,
107
108 };
109
110 static void setup_led_cdev(void)
111 {
112     /*1. 为cdev结构体分配空间*/
113     s5pv_led_dev->led_cdev = cdev_alloc();
114
115     /*2. 初始化cdev结构体*/
116     cdev_init(s5pv_led_dev->led_cdev, &led_fops);
117
118     /*3. 注册cdev,加载到内核哈希表中*/
119     cdev_add(s5pv_led_dev->led_cdev, MKDEV(led_major, 0), 1);
120
121 }
122
123 /*2. 实现模块加载函数*/
124 static int __init led_init(void)
125 {
126     dev_t devno;
127     int ret;
128     /*1. 新的申请主设备号的方法*/
129     if(led_major)
130     {
131         /*静态申请*/
132         devno = MKDEV(led_major, 0);
133         register_chrdev_region(devno, 1, "led");
134     }
135     else
136     {
137         /*动态申请*/
138         alloc_chrdev_region(&devno, 0, 1, "led");
139         led_major = MAJOR(devno);
140     }
141
142     /*2. 为本地结构体分配空间*/
143
144         /*
145         **    param1: 大小
146         **    param2:    标号: GFP_KERNEL--->表示如果分配不成功,则休眠
147         */
148     s5pv_led_dev = kmalloc(sizeof(struct led_device), GFP_KERNEL);
149     if (!s5pv_led_dev)
150     {
151         printk(KERN_ERR "zbzhuang NO memory for malloc!\n");
152         ret = -ENOMEM;
153         goto out_err_1;
154     }
155
156     /*3. 构建struct cdev结构体*/
157     setup_led_cdev();
158
159
160     /*4. 创建设备文件*/
161     /*
162     **    param1:    struct class
163     **    param2:    父类, 一般为NULL
164     **    param3:    dev_t ---> 表示一个设备号, 是一个无符号32位整形
165     **                        其中高12位为主设备号, 低20为次设备号
166     **            如何操作设备号: MKDEV(major, minor)根据主设备号和次设备号组成一个设备号
167     */
168
169     s5pv_led_dev->led_class = class_create(THIS_MODULE, "led_class");
170     if (IS_ERR(s5pv_led_dev->led_class)) {
171         printk(KERN_ERR "zbzhuang class_create() failed for led_class\n");
172         ret = -EINVAL;
173         goto out_err_2;
174     }
175
176
177     s5pv_led_dev->led_device = device_create(s5pv_led_dev->led_class, NULL, MKDEV(led_major, 0), NULL, "led1");    // 创建设备文件/dev/led1
178     if (IS_ERR(s5pv_led_dev->led_device)) {
179         printk(KERN_ERR "zbzhuang device_create failed for led_device\n");
180         ret = -ENODEV;
181         goto out_err_3;
182     }
183
184     return 0;
185
186 out_err_3:
187     class_destroy(s5pv_led_dev->led_class);
188
189 out_err_2:
190     cdev_del(s5pv_led_dev->led_cdev);
191     kfree(s5pv_led_dev);
192
193 out_err_1:
194     unregister_chrdev_region(MKDEV(led_major, 0), 1);
195     return ret;
196
197 }
198
199 /*3. 实现模块卸载函数*/
200 static void __exit led_exit(void)
201 {
202     unregister_chrdev_region(MKDEV(led_major, 0), 1);
203     device_destroy(s5pv_led_dev->led_class, MKDEV(led_major, 0));
204     class_destroy(s5pv_led_dev->led_class);
205     cdev_del(s5pv_led_dev->led_cdev);
206     kfree(s5pv_led_dev);
207 }
208
209 /*4. 模块许可声明*/
210 module_init(led_init);
211 module_exit(led_exit);
212 MODULE_LICENSE("GPL");

Android.mk

 1 LOCAL_PATH:= $(call my-dir)
 2 include $(CLEAR_VARS)
 3
 4 LOCAL_MODULE_TAGS := optional
 5
 6 LOCAL_MODULE:= libled_jni
 7
 8 LOCAL_SRC_FILES:=  9   led_jni.cpp
10
11 LOCAL_SHARED_LIBRARIES := 12     libutils liblog
13
14 LOCAL_C_INCLUDES += 15     $(JNI_H_INCLUDE)
16
17 include $(BUILD_SHARED_LIBRARY)

执行:       mmm mytest/led_jni/   之后会生成动态库放在  out/target/product/msm8916_64/obj/lib/libled_jni.so

将这个库推送到平板电脑就可以通过这个库去调用驱动。

内核的驱动文件:kernel/drivers/input/misc/led.c

  1 /*1. 头文件*/
  2 #include<linux/init.h>
  3 #include<linux/module.h>
  4 #include<linux/fs.h>
  5 #include<linux/device.h>
  6 #include<linux/slab.h>
  7 #include<linux/cdev.h>
  8 #include<asm/uaccess.h>
  9 #include<asm/io.h>
 10
 11 static unsigned int led_major=0;
 12 volatile unsigned long *gpc0con = NULL;
 13 volatile unsigned long *gpc0dat = NULL;
 14
 15 struct led_device{
 16     struct class *led_class ;    //表示一类设备, 存储某些信息
 17     struct device *led_device ;    //表示一个设备
 18     struct cdev  *led_cdev;
 19     unsigned int val;
 20
 21 };
 22
 23 struct led_device *s5pv_led_dev;
 24
 25
 26 /*可用于查询LED的状态*/
 27 static ssize_t led_read(struct file *file, char __user *buf, size_t count, loff_t *opps)
 28 {
 29     int ret;
 30
 31     ret = copy_to_user(buf, &s5pv_led_dev->val, count);
 32     if(ret>0)
 33     {
 34         printk(KERN_ERR "zbzhuang### copy to user failed!\n");
 35         return ret;
 36     }
 37
 38     printk(KERN_INFO "zbzhuang### val=%d\n", s5pv_led_dev->val);
 39
 40     return ret?0:count;
 41 }
 42 static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *opps)
 43 {
 44     int ret;
 45
 46     /*拷贝成功,返回0; 拷贝失败, 返回没有被拷贝的字节数*/
 47     ret = copy_from_user(&s5pv_led_dev->val, buf, count);
 48     if(ret>0)
 49     {
 50         printk(KERN_ERR "zbzhuang### copy from user failed!\n");
 51         return ret;
 52     }
 53
 54     if(s5pv_led_dev->val)
 55     {
 56         /*点亮LED*/
 57         //*gpc0dat |= ((0x1<<3)|(0x1<<4));
 58         printk(KERN_ERR "zbzhuang### led on\n");
 59     }
 60     else
 61     {
 62         /*熄灭LED*/
 63 //        *gpc0dat &= ~((0x1<<3)|(0x1<<4));
 64         printk(KERN_ERR "zbzhuang### led off\n");
 65     }
 66
 67     return ret?0:count;
 68 }
 69
 70
 71
 72 static int led_open(struct inode *inode, struct file *file)
 73 {
 74 #if 0
 75     /*1. 将物理地址映射为虚拟地址*/
 76     gpc0con = ioremap(0xE0200060, 8);
 77     gpc0dat = gpc0con +1;
 78
 79     /*2. 初始化GPC0_3,4引脚功能为输出*/
 80     *gpc0con &= ~((0xf<<12)|(0xf<<16));
 81     *gpc0con |=  ((0x1<<12)|(0x1<<16));
 82 #endif
 83     printk(KERN_ERR "zbzhuang### -----------%s-------------\n",__FUNCTION__);
 84
 85     return 0;
 86 }
 87
 88 static int led_close(struct inode *inode, struct file *file)
 89 {
 90 #if 0
 91
 92     *gpc0con &= ~((0xf<<12)|(0xf<<16));
 93     iounmap(gpc0con);
 94 #endif
 95     printk(KERN_ERR "zbzhuang### ------------%s-----------------\n",__FUNCTION__);
 96
 97     return 0;
 98
 99 }
100
101 /*硬件操作方法*/
102 struct file_operations led_fops={
103     .owner = THIS_MODULE,    //当前模块所用
104     .open  = led_open,
105     .write = led_write,
106     .read  = led_read,
107     .release = led_close,
108
109 };
110
111 static void setup_led_cdev(void)
112 {
113     /*1. 为cdev结构体分配空间*/
114     s5pv_led_dev->led_cdev = cdev_alloc();
115
116     /*2. 初始化cdev结构体*/
117     cdev_init(s5pv_led_dev->led_cdev, &led_fops);
118
119     /*3. 注册cdev,加载到内核哈希表中*/
120     cdev_add(s5pv_led_dev->led_cdev, MKDEV(led_major, 0), 1);
121
122 }
123
124 /*2. 实现模块加载函数*/
125 static int __init led_init(void)
126 {
127     dev_t devno;
128     int ret;
129     /*1. 新的申请主设备号的方法*/
130     if(led_major)
131     {
132         /*静态申请*/
133         devno = MKDEV(led_major, 0);
134         register_chrdev_region(devno, 1, "led");
135     }
136     else
137     {
138         /*动态申请*/
139         alloc_chrdev_region(&devno, 0, 1, "led");
140         led_major = MAJOR(devno);
141     }
142
143     /*2. 为本地结构体分配空间*/
144
145         /*
146         **    param1: 大小
147         **    param2:    标号: GFP_KERNEL--->表示如果分配不成功,则休眠
148         */
149     s5pv_led_dev = kmalloc(sizeof(struct led_device), GFP_KERNEL);
150     if (!s5pv_led_dev)
151     {
152         printk(KERN_ERR "zbzhuang NO memory for malloc!\n");
153         ret = -ENOMEM;
154         goto out_err_1;
155     }
156
157     /*3. 构建struct cdev结构体*/
158     setup_led_cdev();
159
160
161     /*4. 创建设备文件*/
162     /*
163     **    param1:    struct class
164     **    param2:    父类, 一般为NULL
165     **    param3:    dev_t ---> 表示一个设备号, 是一个无符号32位整形
166     **                        其中高12位为主设备号, 低20为次设备号
167     **            如何操作设备号: MKDEV(major, minor)根据主设备号和次设备号组成一个设备号
168     */
169
170     s5pv_led_dev->led_class = class_create(THIS_MODULE, "led_class");
171     if (IS_ERR(s5pv_led_dev->led_class)) {
172         printk(KERN_ERR "zbzhuang class_create() failed for led_class\n");
173         ret = -EINVAL;
174         goto out_err_2;
175     }
176
177
178     s5pv_led_dev->led_device = device_create(s5pv_led_dev->led_class, NULL, MKDEV(led_major, 0), NULL, "led1");    // 创建设备文件/dev/led1
179     if (IS_ERR(s5pv_led_dev->led_device)) {
180         printk(KERN_ERR "zbzhuang device_create failed for led_device\n");
181         ret = -ENODEV;
182         goto out_err_3;
183     }
184
185     return 0;
186
187 out_err_3:
188     class_destroy(s5pv_led_dev->led_class);
189
190 out_err_2:
191     cdev_del(s5pv_led_dev->led_cdev);
192     kfree(s5pv_led_dev);
193
194 out_err_1:
195     unregister_chrdev_region(MKDEV(led_major, 0), 1);
196     return ret;
197
198 }
199
200 /*3. 实现模块卸载函数*/
201 static void __exit led_exit(void)
202 {
203     unregister_chrdev_region(MKDEV(led_major, 0), 1);
204     device_destroy(s5pv_led_dev->led_class, MKDEV(led_major, 0));
205     class_destroy(s5pv_led_dev->led_class);
206     cdev_del(s5pv_led_dev->led_cdev);
207     kfree(s5pv_led_dev);
208 }
209
210 /*4. 模块许可声明*/
211 module_init(led_init);
212 module_exit(led_exit);
213 MODULE_LICENSE("GPL");

修改kconfig和makefile,在内核当中添加:

makefile

kconfig:

修改这个文件将驱动编译进内核:kernel/arch/arm64/configs/msm_defconfig  与msm-perf_deconfig

之后使用adb工具将重新编译的boot.imge镜像重新烧录就可以了啊。

看看log的输出验证一下结果:

android studio查看log的结果:

adb查看log的结果:

从app到jni到kernel,整个调用的过程成功。

如果出现打开设备失败的话在system/core/rootdir/uevent.rc中添加设备节点的权限777即可。

时间: 2024-12-28 11:04:58

Android字符设备驱动开发基于高通msm8916【原创 】的相关文章

arm-linux字符设备驱动开发之---简单字符设备驱动

一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用驱动程序: 1.字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据.字符设备是面向流的设备,常见的字符设备有鼠标.键盘.串口.控制台和LED设备等.2.块设备:是指可以从设备的任意位置读取一定长度数据的设备.块设备包括硬盘.磁盘.U盘和SD卡等. 每一个字符设备或块设备都在/dev目录下对应一个设备文件.linux用户程序通过设备文件(或称设备节点)来使用驱动程序操作字符设备和

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

目录 博客说明 开发环境 1. Linux字符设备驱动的组成 1.1 字符设备驱动模块加载与卸载函数 1.2 字符设备驱动的file_operations 结构体中的成员函数 2. 字符设备驱动--设备号注册卸载 2.1 设备号注册 2.2 设备号注销 3. 字符设备驱动--文件操作 参考资料 示例代码 @(从Linux内核LED驱动来理解字符设备驱动开发流程) 博客说明 撰写日期 2018.12.08 完稿日期 2019.10.06 最近维护 暂无 本文作者 multimicro 联系方式 [

Linux字符设备驱动开发的一般方法

.output_wrapper pre code { font-family: Consolas, Inconsolata, Courier, monospace; display: block !important; white-space: pre !important; overflow: auto !important } .output_wrapper a:hover { text-decoration: underline; color: rgb(0, 96, 100) } .out

linux 字符设备驱动开发详解

一.设备的分类及特点 1.字符设备 字符设备是面向数据流的设备,没有请求缓冲区,对设备的存取只能按顺序按字节的存取而不能随机访问.    Linux下的大多设备都是字符设备.应用程序是通过字符设备节点来访问字符设备的.通常至少需要实现 open, close, read, 和 write 等系统调用.    设备节点一般都由mknod命令都创建在/dev目录下,包含了设备的类型.主/次设备号以及设备的访问权限控制等,如:crw-rw----  1 root  root 4, 64 Feb 18

linux驱动学习(1)——字符设备驱动开发

(一)驱动程序介绍 (a)Linux驱动程序学习 知识结构: 1. Linux驱动程序设计模式(40%) 2. 内核相关知识(30%) 3. 硬件相关知识(30%) (b)驱动分类: ①字符设备: 字符设备是一种按字节来访问的设备,字符驱动则负责驱动字符设备,这样的驱动通常实现 open, close,read和 write 系统调用. ②块设备: 在大部分的 Unix 系统, 块设备不能按字节处理数据,只能一次传送一个或多个长度是512字节( 或一个更大的 2 次幂的数 )的整块数据,而Lin

(57)Linux驱动开发之三Linux字符设备驱动

1.一般情况下,对每一种设备驱动都会定义一个软件模块,这个工程模块包含.h和.c文件,前者定义该设备驱动的数据结构并声明外部函数,后者进行设备驱动的具体实现. 2.典型的无操作系统下的逻辑开发程序是:这种三层的裸机驱动模型是足够满足低耦合.高内聚的特点的. 3.当有操作系统存在时,设备驱动成为了连接硬件和内核的桥梁,这时候的设备驱动对外表现为操作系统的API,与直接裸机开发不同,裸机开发时的设备驱动是应用工程师的API.如果设备驱动都按照操作系统给出的独立于设备的接口而设计,应用程序将可以使用统

Linux 设备驱动开发 —— platform设备驱动应用实例解析

前面我们已经学习了platform设备的理论知识Linux 设备驱动开发 -- platform 设备驱动 ,下面将通过一个实例来深入我们的学习. 一.platform 驱动的工作过程 platform模型驱动编程,platform 驱动只是在字符设备驱动外套一层platform_driver 的外壳. 在一般情况下,2.6内核中已经初始化并挂载了一条platform总线在sysfs文件系统中.那么我们编写platform模型驱动时,需要完成两个工作: a -- 实现platform驱动 架构就

SylixOS字符设备驱动框架

1.概述 本文档主要介绍SylixOS中字符设备驱动框架,适用于在SylixOS集成开发环境下进行字符设备驱动开发的学习. 注:文中xxx是指具体设备名,编写对应驱动时,自行命名(如RTC.COMPASS等). 2.SylixOS字符设备驱动简介 字符设备是指只能以字节为单位进行读写的设备,读取数据需按照先后顺序,不能随机读取设备内存中某一数据.常见的字符设备如:鼠标.键盘.串口等. 在SylixOS中,每个字符设备都会在/dev目录下对应一个设备文件,用户程序可通过设备文件(或设备节点)来使用

《Linux设备驱动开发具体解释(第3版)》(即《Linux设备驱动开发具体解释:基于最新的Linux 4.0内核》)网购链接

<Linux设备驱动开发具体解释:基于最新的Linux 4.0内核> china-pub   spm=a1z10.3-b.w4011-10017777404.30.kvceXB&id=521111707813&rn=4cf013961288ab7c4dfd2016aeb21fa8&abbucket=5">天猫     dangdang   京东 China-pub 8月新书销售榜 推荐序一 技术日新月异,产业斗转星移,滚滚红尘,消逝的事物太多,新事物的诞