linux之misc及使用misc建立字元裝置
1:linux字元裝置及udev
1.1字元裝置
字元裝置就是:一個一個位元組來進行訪問的,不能對字元裝置進行隨機讀寫。簡單字元裝置建立例項如下:
#include <linux/module.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <linux/cdev.h> #include <linux/device.h> #define LED_MAJOR 241 /*預設的LED_MAJOR的主裝置號*/ static int led_major = LED_MAJOR; volatile unsigned long *gpbcon = NULL ; volatile unsigned long *gpbdat = NULL ; volatile unsigned long *gpbup = NULL ; struct cdev cdev; /*cdev結構體*/ static int led_drv_open(struct inode *inode, struct file *file) { printk("first_drv_open\n"); return 0; } static ssize_t led_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) { return 0; } static struct file_operations led_drv_fops = { .owner = THIS_MODULE, /* 這是一個巨集,推向編譯模組時自動建立的__this_module變數 */ .open = led_drv_open, .write = led_drv_write, }; /*初始化並註冊cdev*/ static void led_setup_cdev(void) { int err, devno = MKDEV(led_major, 0);//index 為從裝置號 cdev_init(&cdev, &led_drv_fops); cdev.owner = THIS_MODULE; cdev.ops = &led_drv_fops; err = cdev_add(&cdev, devno, 1);//devno 為第一個裝置號,1為數量 if (err) printk(KERN_NOTICE "Error %d adding", err); } static int led_drv_init(void) { int result; dev_t devno; devno=MKDEV(led_major,0); if(led_major)//靜態申請裝置號 result=register_chrdev_region(devno,1,"led1_dev"); else { result = alloc_chrdev_region(&devno,0,1,"led1_dev");//動態申請裝置號 led_major = MAJOR(devno); } if(result<0) { printk (KERN_WARNING "hello: can't get major number %d\n", led_major); return result; } led_setup_cdev(); return 0; } static void led_drv_exit(void) { cdev_del(&cdev); // 登出cdev unregister_chrdev_region(MKDEV(led_major,0),1);//釋放裝置號 } module_init(led_drv_init); module_exit(led_drv_exit); MODULE_LICENSE("GPL");
上面是一個字元裝置的框架,沒有實質性的東西,如:沒有實現file_operations結構中的成員函式,同時led_drv_open(),led_drv_write()沒有做任何東西。
現在將建立一個基本的字元裝置過程總結如下:
A:建立裝置號。MKDEV(major_no,0),其值為一個整數。因為linux中使用裝置號來關聯相應的裝置和裝置對於的驅動程式。
B:註冊裝置號。register_chrdev_region(devno,1,"led1_dev")或者alloc_chrdev_region(&devno,0,1,"led1_dev");//動態申請裝置號
C:初始化並關聯file_operations結構體。 cdev_init(&cdev, &led_drv_fops);其中file_operations結構體中主要定義對字元裝置的操作函式。
D:新增字元裝置到核心。int cdev_add(struct cdev *p, dev_t dev, unsigned count),
E:移除字元裝置及裝置號。 cdev_del(&cdev); unregister_chrdev_region(MKDEV(led_major,0),1);完成於cdev_add()和register_chrdev_region()相反的工作
上面涉及到的API可以在函式linux/fs/char_dev.c中找到定義。
按照上面的方法可以建立一個主裝置號為:241,次裝置號為:0的一個字元裝置。
可以講上面的程式碼編譯進核心:.
其方式如下:
將上面的程式碼儲存到test.c中
在[[email protected] /opt/FriendlyARM/linux-2.6.32.2/drivers]$ 目下下面建立檔案件 mkdir suiyuan
之後在suiyuan中建立makefile,將上面的程式碼直接編譯進核心,因此可以不需要kconfig檔案
其makefile的內容為:
obj-$(CONFIG_SUIYUAN_HELLOWORLD)+= suiyuan_hello_module.o
#obj-$(CONFIG_STATIC_SUIYUAN_HELLOWORLD) += static_suiyuan_hello_module.o
obj-m+= static_suiyuan_hello_module.o
obj-y+= class_device_driver.o
obj-y+= leds.o
obj-y+= test.o
#obj-y+= ledsv2.o
#obj-y+= ledsv3.o
表示將obj-y表示將test編譯進核心,在載入內的時候將該字元裝置載入進核心。
同時在再修改/opt/FriendlyARM/linux-2.6.32.2/drivers目錄下面的Makefile檔案,在Makefile檔案的末尾新增obj-y += suiyuan/ 表示對suiyuan目錄下面的所有檔案進行編譯。
有上面可以知道kconfig檔案費必須,同時只有在需要將核心編譯為模組的時候,才需要編寫配置檔案kconfig。
重新編譯核心,將編譯好的核心轉化為uImage 格式。
對於上面建立的字元裝置在使用的時候需要,建立裝置檔案。建立裝置檔案使用命令:mknod
mknod的使用方式如下:
定義:mknod - make block or character special files
語法:mknod /dev/ttyUSBn c Major Minor
1,n要等於次裝置號Minor,且要小於主裝置號Major.
2, c:面向字元裝置(b:面向塊裝置,如:磁碟、軟盤、磁帶;其他裝置基本都為字元裝置).
在上面的程式碼中,我們的主從裝置號為:241和0。故建立裝置節點:mknod /dev/suiyuan c 241 0
之後可以[@mini2440 /dev]#cat test
led_drv_open
cat: read error: Invalid argument
如上面的結果,表示應用程式cat在訪問裝置檔案的時候,呼叫了file_operations 資料結構中的open()函式。應用程式和底層驅動就此聯絡了起來。
1.2 udev是什麼及其作用
udev檔案系統是針對2.6核心,提供一個基於使用者空間的動態裝置節點管理和命名的使用程式,udev 以守護程序的形式執行,通過偵聽核心發出來的 uevent 來動態的管理/dev
目錄下的裝置檔案。:udev是一種工具,它能夠根據系統中的硬體裝置的狀態動態更新裝置檔案,包括裝置檔案的建立,刪除等。裝置檔案通常放在/dev目錄下。使用udev後,在/dev目錄下就只包含系統中真正存在的裝置。udev動態的根據根檔案系統目錄下面的/sys檔案主動的建立裝置檔案,故在編寫好裝置驅動的時候,不需要再手動通過命令mknod來建立裝置檔案。udev涉及到/sys目錄下面的檔案,而/sys涉及到linux裝置驅動模式。以後自己在總結這方面的東西。udev是一個使用者程式(user-mode
daemon)。
現在就如何通過udev動態的建立/dev/目錄下面的檔案為例進行說明。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/system.h> /* cli(), *_flags */
#include <asm/uaccess.h> /* copy_*_user */
#include <asm/io.h>
#include <linux/ioport.h>
#include <linux/device.h> /* for sys mdev */
#define LEDS_DEBUG
#undef PDEBUG /* undef it, just in case */
#ifdef LEDS_DEBUG
# ifdef __KERNEL__
/* This one if debugging is on, and kernel space */
# define PDEBUG(fmt, args...) printk( KERN_EMERG "leds: " fmt, ## args)
# else
/* This one for user space */
# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)
# endif
#else
# define PDEBUG(fmt, args...) /* not debugging: nothing */
#endif
#define LEDS_MAJOR 400
#define LEDON 0
#define LEDOFF 1
#define BUZON 2
#define BUZOFF 3
#define GPBCON 0x56000010
struct leds_dev {
unsigned char value; /* When LED lighted, its value bit is 1, otherwise 0 */
struct cdev cdev;
};
static struct class *leds_class;
static struct leds_dev *leds_devp;
static int leds_major = LEDS_MAJOR;
static void *virtaddr;
static int leds_open(struct inode *inode, struct file *filp)
{
printk("file_operations:open\n");
return 0;
}
static int leds_release(struct inode *inode, struct file *filp)
{
printk("file_operations:open\n");
return 0;
}
static ssize_t leds_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
printk("file_operations:read\n");
return 0;
}
static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
printk("file_operations:write\n");
return 0;
}
static int leds_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
printk("file_operations:ioctl\n");
return 0;
}
static struct file_operations leds_fops =
{
.owner = THIS_MODULE,
.read = leds_read,
.ioctl = leds_ioctl,
.open = leds_open,
.release = leds_release,
.write = led_write
};
static void leds_setup_cdev(struct leds_dev *dev, int index)
{
int err, devno = MKDEV(leds_major, index);
cdev_init(&dev->cdev, &leds_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &leds_fops;
err = cdev_add(&dev->cdev, devno, 1);
if (err)
{
printk(KERN_NOTICE "Error adding LEDS\n");
}
}
static int leds_init(void)
{
int result;
dev_t devno = MKDEV(leds_major, 0);
PDEBUG("enter led_init\n");
if (leds_major)
{
result = register_chrdev_region(devno, 1, "leds");
}
else
{
result = alloc_chrdev_region(&devno, 0, 10, "leds");
leds_major = MAJOR(devno);
}
if (result < 0)
{
return result;
}
leds_devp = kmalloc(sizeof(struct leds_dev), GFP_KERNEL);
memset(leds_devp, 0, sizeof(struct leds_dev));
leds_setup_cdev(leds_devp, 0);
leds_class = class_create(THIS_MODULE, "suiyuan_leds_class");
if (IS_ERR(leds_class))
{
printk(KERN_WARNING "failed in creating class\n");
}
// class_device_create(leds_class, NULL, devno, NULL, "suiyuan_leds");//這個函式在較早的額核心可以使用
device_create(leds_class, NULL, devno, NULL, "suiyuan_leds"); //高版本核心可以使用我的是2.6.32.2-FriendlyARM
printk("leds initialized\n");
return 0;
}
void suiyuan_leds_cleanup(void)
{
cdev_del(&leds_devp->cdev);
kfree(leds_devp);
unregister_chrdev_region(MKDEV(leds_major, 0), 1);
//class_device_destroy(leds_class, MKDEV(leds_major, 0));
device_destroy(leds_class, MKDEV(leds_major, 0));
class_destroy(leds_class);
printk("leds driver unloaded\n");
}
module_init(leds_init);
module_exit(suiyuan_leds_cleanup);
MODULE_AUTHOR("suiyuan");
MODULE_LICENSE("Dual BSD/GPL");
上面的程式碼建立了一個簡單字元的裝置,但是在最後使用函式class_create()和device_create()用來分別在/sys和/dev目錄下面建立相應的資料夾及檔案。
首先在:在/sys目錄中
[@mini2440 /sys/dev/char]#ls
10:130 116:16 1:11 400:0 //是自己建立的裝置檔案
[@mini2440 /sys/class]#ls
bdi input net sound
block leds rtc suiyuan_leds_class 是上面的函式class_create()所建立
class_suiyuan mem scsi_device tty
firmware misc scsi_disk vc
graphics mmc_host scsi_generic video4linux
i2c-dev mtd scsi_host vtconsole
在./dev目錄下面:
crw-rw---- 1 0 0 400, 0 Feb 16 20:11 suiyuan_leds 是device_create()函式所建立的裝置檔案有對應的主從裝置號
在執行 cat
[@mini2440 /dev]#cat suiyuan_leds
file_operations:open
file_operations:read
file_operations:open
表示呼叫了file_operations中的驅動函式,open()和read().
現在在說說 udev.conf檔案,現在建立/etc/udev.conf檔案在其檔案中新增
如下類容:suiyuan_leds 0:5 0666 =MyTestLed #MyTestLed為裝置suiyuan_leds的別名。在載入檔案系統的時候,將執行udev.conf中的內容。
之後重現載入rootfs 。此時的/dev/如下:
[@mini2440 /dev]#ls -al
total 4
drwxr-xr-x 3 0 0 0 Feb 17 08:34 .
drwxr-xr-x 16 0 0 4096 Feb 13 11:08 ..
crw-rw-rw- 1 0 5 400, 0 Feb 17 08:34 MyTestLed
同時也可以參考mini2440上面提供的udev.conf 檔案中的內容。
2:misc裝置
下面是一個misc裝置的主要組成,結構相對明確。
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
static int leds_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
printk ("misc leds_ioctl\n");
return 0;
}
static int leds_open(struct inode *inode, struct file *filp)
{
printk ("misc in leds_open\n");
return 0;
}
static int leds_release(struct inode *inode, struct file *filp)
{
printk ("misc leds_release\n");
return 0;
}
static ssize_t leds_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
printk ("misc leds_read\n");
return 1;
}
static struct file_operations leds_fops =
{
.owner = THIS_MODULE,
.read = leds_read,
.ioctl = leds_ioctl,
.open = leds_open,
.release = leds_release
};
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "misc_leds", //此名稱將顯示在/dev目錄下面
.fops = &leds_fops,
};
static int __init dev_init(void)
{
int ret = misc_register(&misc);
printk ("misc leds initialized\n");
return ret;
}
static void __exit dev_exit(void)
{
misc_deregister(&misc);
printk("misc leds unloaded\n");
}
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("suiyuan");
將上面的程式碼儲存為suiyuanMisc.c,並編譯進核心。
[[email protected] /opt/FriendlyARM/linux-2.6.32.2]$make
CHK include/linux/version.h
make[1]: `include/asm-arm/mach-types.h' is up to date.
CHK include/linux/utsrelease.h
SYMLINK include/asm -> include/asm-arm
CALL scripts/checksyscalls.sh
CHK include/linux/compile.h
CC drivers/suiyuan/suiyuanMisc.o
LD drivers/suiyuan/built-in.o
LD drivers/built-in.o
LD vmlinux.o
MODPOST vmlinux.o
WARNING: vmlinux: 'ledoff' exported twice. Previous export was in vmlinux
WARNING: vmlinux: 'ledon' exported twice. Previous export was in vmlinux
GEN .version
CHK include/linux/compile.h
UPD include/linux/compile.h
CC init/version.o
LD init/built-in.o
LD .tmp_vmlinux1
KSYM .tmp_kallsyms1.S
AS .tmp_kallsyms1.o
LD .tmp_vmlinux2
KSYM .tmp_kallsyms2.S
AS .tmp_kallsyms2.o
LD vmlinux
SYSMAP System.map
SYSMAP .tmp_System.map
OBJCOPY arch/arm/boot/Image
Kernel: arch/arm/boot/Image is ready
GZIP arch/arm/boot/compressed/piggy.gz
AS arch/arm/boot/compressed/piggy.o
LD arch/arm/boot/compressed/vmlinux
OBJCOPY arch/arm/boot/zImage
Kernel: arch/arm/boot/zImage is ready
Building modules, stage 2.
MODPOST 17 modules
WARNING: vmlinux: 'ledoff' exported twice. Previous export was in vmlinux
WARNING: vmlinux: 'ledon' exported twice. Previous export was in vmlinux
將zImage轉化為uImage,並通過nfs下載到內容,並啟動。可以在啟動的時候看到:misc leds initialized 資訊。
之後可以在mini2440的/dev/目錄下面看到。
有如下裝置:crw-rw---- 1 0 0 10, 56 Feb 18 14:44 misc_leds_suiyuan 主裝置號為:10,次裝置號為:56
同時可以看看檔案:/proc/misc,其檔案中記載了系統中所載入misc裝置。
[@mini2440 /proc]#cat misc
53 network_throughput
54 network_latency
55 cpu_dma_latency
56 misc_leds_suiyuan
57 leds_03
130 watchdog
58 camera
59 adc
60 pwm
61 buttons
62 leds
63 backlight
misc總結如下:
misc字元裝置,該類裝置使用同一個主裝置號10,misc字元裝置使用的資料結構
struct miscdevice {
int minor;
const char *name;
struct file_operations *fops;
struct list_head list;
struct device *dev;
struct class_device *class;
char devfs_name[64];
};
雜項裝置(misc device)
在 Linux 核心的include\linux\miscdevice.h檔案,要把自己定義的misc device從裝置定義在這裡。其實是因為這些字元裝置不符合預先確定的字元裝置範疇,所有這些裝置採用主裝置號10 ,一起歸於misc device,其實misc_register就是用主裝置號10呼叫register_chrdev()的。也就是說,misc裝置其實也就是特殊的字元裝置。
misc_device是特殊的字元裝置。註冊驅動程式時採用misc_register函式註冊,此函式中會自動建立裝置節點,即裝置檔案。無需mknod指令建立裝置檔案。因為misc_register()會呼叫class_device_create()或者device_create()。
因此使用misc建立字元裝置最簡單方便。
相關推薦
linux之misc及使用misc建立字元裝置
1:linux字元裝置及udev 1.1字元裝置 字元裝置就是:一個一個位元組來進行訪問的,不能對字元裝置進行隨機讀寫。簡單字元裝置建立例項如下: #include <linux/module.h> #include <linux/kernel
嵌入式Linux驅動開發(四)——字元裝置驅動之中斷方式以及中斷方式獲取按鍵值
之前我們完成了關於通過查詢的方式獲取按鍵鍵值的驅動程式,可以參考:嵌入式Linux開發——裸板程式之中斷控制器。 雖然讀取鍵值沒有什麼問題,但是測試程式佔用CPU過高,一直在不斷的查詢,資源消耗過大,這個問題非常嚴重,我們必須要來解決一下。但是在解決這個問題之前,我們先來思考一個問題,除
謝煙客---------Linux之ip及ifcfg配置網絡連接
linuxip 網絡層協議ip地址 點分十進制分為4段,範圍 0-255ip分類A 占據1段,最左側一段第一位固定為0 0 000 0000 - 0 111 1111 0 - 127:其中0為網絡,127 本地主機內部通信。 網絡數: 2^7-2=126個 每個網絡中的主機數: 2^2
【linux之find及awk】
user con linux art mtime follow proc lock 樣式 一、find命令 find 精確查找,根據提供的條件或組合條件進行查找,遍歷所有文件,因此速度比較慢。 語法: find 目錄 條件 動作 默認目錄是當前目錄默認條件是所有條件默認動作
linux之程序及服務的控制
什麼是程序? 程序就是系統中正在執行的程式 1.圖形的程序檢視方式 gnome-system-moniter 輸入指令後,就打開了如下圖的程序管理器,相當於iwindows的工作管理員,可以檢視正在執行的程式 提示: “ls /proc“:檢視程序資訊 2.檢視程
linux之檔案及目錄許可權的相關操作
1.檔案許可權控制 首先我們先搞清一個問題:許可權存在的意義? 在我們的印象中檔案是用來存放實際資料的,目錄是用來存放檔案的,但其實目錄的主要作用是用來記錄檔名列表,檔名與目錄有強烈的關聯。許可權就是使用者或者其他人對這些目錄以及檔案能進行些什麼操作。 檔案的檢視 當我們建立
ARM學習筆記之驅動程式篇七----字元裝置驅動
1.11 字元裝置驅動模型 在任何一種驅動模型中,裝置都會用核心中的一種結構來描述。字元裝置在核心中使用struct cdev來描述。 struct cdev{ struct kobject kobj; struct module *owner; const stru
張大帥學Linux之使用者及使用者組管理
張大帥學Linux之使用者及使用者組管理 標籤(空格分隔): Linux 使用者概述及管理 Linux系統是一個多使用者多工系統。通過授予不同的使用者許可權,可以讓不同的使用者執行指定的任務及訪問不同的資源。當Linux系統擁有越來越多的使用者時,管理任務會更復雜,因此需
Linux Kernel 學習筆記5:字元裝置
(本章基於:linux-4.4.0-37) 本章介紹如何註冊一個字元裝置,並通過裝置節點對這個字元裝置進行open、close、read、write等操作。 一、字元設備註冊 相關標頭檔案:linux/cdev.h 1、初始化 void cdev_init(struct
oracle linux 6.9通過UDEV建立裸裝置
oracle rac安裝前系統環境準備,通過UDEV建立共享磁碟裸裝置1、建立oracle rac系統所需的使用者和組groupadd -g 501 oinstall groupadd -g 502 dba groupadd -g 504 asmadmin groupadd -g 506 asmdba gro
小猿圈linux之網路程式設計--socket建立
網路程式設計離不開socket,小猿圈這篇詳解一下socket建立,仔細學完這篇對你認識網路底層的東西有著很重要的作用,同時即便有
linux driver ------ 字元裝置驅動之“ 建立裝置節點流程 ”
在字元裝置驅動開發的入門教程中,最常見的就是用device_create()函式來建立裝置節點了,但是在之後閱讀核心原始碼的過程中卻很少見device_create()的蹤影了,取而代之的是device_register()與device_add(),將device_create()函式展開不難發現:其實de
linux驅動開發10之misc及蜂鳴器驅動
1.板載蜂鳴器驅動測試 1.1驅動部分 1)九鼎移植核心已經提供了蜂鳴器驅動原始碼 2)make menuconfig 3)bug排查。修改Makefile中的巨集名,最終可以在系統中看到 /dev/buzzer 由於makefile檔案與Kconfig檔案中的
linux 字元裝置和misc裝置
字元裝置 2.6版本前使用的結構體和函式 <span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span>&l
linux一切皆檔案之tty字元裝置(深入理解sshd建立pty的過程) (五)
一、知識準備 1、在linux中,一切皆為檔案,所有不同種類的型別都被抽象成檔案(比如:塊裝置,socket套接字,pipe佇列) 2、操作這些不同的型別就像操作檔案一樣,比如增刪改查等 3、塊裝置支援隨機訪問,而字元裝置只能依據先後順序來讀取資料。最典型的字元裝置就是tty 二、環境準備 元
Linux 字元裝置驅動結構(二)—— 自動建立裝置節點
上一篇我們介紹到建立裝置檔案的方法,利用cat /proc/devices檢視申請到的裝置名,裝置號。 第一種是使用mknod手工建立:mknod filename type major minor 第二種是自動建立裝置節點:利用u
Linux:驅動之字元裝置工作原理(未完)
字元裝置驅動工作原理 系統整體工作原理 應用層->API->裝置驅動->硬體? API:open、read、write、close等? 驅動原始碼中提供真正的open、read、write、close等函式實體? file_
linux驅動之字元裝置
第一部分:字元裝置工作過程1、系統呼叫和驅動程式的關聯關鍵結構體:struct file_operation;file_operation結構體的每一個成員的名字都對應著一個系統呼叫。使用者程序利用系統呼叫在對裝置檔案進行諸如read/write操作時,系統呼叫通過裝置檔案的主裝置號找到相應的裝置驅動程式,然
Linux裝置驅動之字元裝置驅動---轉
一、linux系統將裝置分為3類:字元裝置、塊裝置、網路裝置。 應用程式呼叫的流程框圖: 三種裝置的定義分別如下, 字元裝置:只能一個位元組一個位元組的讀寫的裝置,不能隨機讀取裝置記憶體中的某一資料,讀取資料需要按照先後順序進行。字元裝置是面向流的裝置,常見的字
3---linux字元裝置之點亮led
概要:上一篇我們編寫了一個非常簡單的字元裝置框架和一個我們自己寫的fops,似乎還是感覺有點索然無味,因此這篇我們搞一個能在現實世界上出現的東西,就是led亮起來 上一篇我們寫了一個字元裝置的框架,因此我們可以拿過來用:2—linux字元裝置進階篇 1.檢視2440原理圖,隨便找一個