第一個(不完整):
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <asm/irq.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/uaccess.h>
/*Comment/uncomment the following line to disable/enable debugging,
OR define(or NOT) it in Makefile.
*/
#define S3C24XX_LEDS_DEBUG
#undef PDEBUG /* undef it, just in case */
#ifdef S3C24XX_LEDS_DEBUG
#ifdef __KERNEL__
/* This one if debugging is on, and kernel space */
#define PDEBUG(fmt, args...) printk( KERN_DEBUG "s3c24xx_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_NAME "leds"
static int leds_major = 0;
module_param (leds_major, int, S_IRUGO | S_IWUGO); //模塊參數
MODULE_PARM_DESC (leds_major, "leds major number");
#define CMD_LED_ON 1
#define CMD_LED_OFF 0
#define GPFBASE 0x56000050
#define GPFCON (GPFBASE + 0x0) //控制寄存器
#define GPFDAT (GPFBASE + 0x4) //數據寄存器
#define GPFUP (GPFBASE +0x8) //上拉電阻
static volatile unsigned long *vGPFBASE = NULL;
#define vGPFCON (vGPFBASE +0)
#define vGPFDAT (vGPFBASE +1)
#define vGPFUP (vGPFBASE +2)
static int s3c24xx_leds_open (struct inode *inode, struct file *filp)
{
PDEBUG ("enter %s %s %d\n", __FILE__, __func__, __LINE__);
//GPFCON, set GPF4~7 output 設置輸出引腳控制
writel ((readl (vGPFCON) & ~0xff00) | 0x5500, vGPFCON);//從內存映射的 I/O 空間讀取數據(readl 從 I/O 讀取 32 位數據 ( 4 字節 )。)
//GPFUP Disable 禁止上拉電阻
writel ((readl (vGPFUP) & ~0xf0) | 0xf0, vGPFUP);
return 0;
}
static ssize_t s3c24xx_leds_read (struct file *filp, char __user * buf, size_t size, loff_t * offset)
{
return 0;
}
static int s3c24xx_leds_close (struct inode *inode, struct file *filp)
{
PDEBUG ("enter %s %s %d\n", __FILE__, __func__, __LINE__);
return 0;
}
static int s3c24xx_leds_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
PDEBUG ("enter %s %s %d\n", __FILE__, __func__, __LINE__);
if (!capable (CAP_SYS_ADMIN)) {
return -EPERM;
}
if (arg < 1 || arg > 4) {
printk (LEDS_NAME " led number error!\n");
return -EINVAL;
}
switch (cmd) {
case CMD_LED_ON:
//點亮
writel (readl (vGPFDAT) & (~(1 << (4 + arg - 1))), vGPFDAT);
break;
case CMD_LED_OFF:
//熄滅
writel (readl (vGPFDAT) | (1 << (4 + arg - 1)), vGPFDAT);
break;
default:
printk (LEDS_NAME " Can't support command!\n");
return -EINVAL;
break;
}
return 0;
}
static struct file_operations s3c24xx_leds_fops = {
.owner = THIS_MODULE,
.open = s3c24xx_leds_open,
.release = s3c24xx_leds_close,
.ioctl = s3c24xx_leds_ioctl,
.read = s3c24xx_leds_read,
};
static void __exit hello_exit (void)
{
unregister_chrdev (leds_major, LEDS_NAME); //註銷
printk (LEDS_NAME " driver exit!\n");
}
static int __init hello_init (void)
{
int ret = -1;
PDEBUG ("enter %s %s %d\n", __FILE__, __func__, __LINE__);
ret = register_chrdev (leds_major, LEDS_NAME, &s3c24xx_leds_fops); //內核註冊函數
if (ret < 0) {
printk (LEDS_NAME " can't register major number\n");
return -EINVAL;
}
if (ret > 0) {
leds_major = ret;
}
vGPFBASE = (volatile unsigned long *) ioremap (GPFBASE, 0x20); //物理地址映射到虛擬地址
if (!vGPFBASE) {
printk (LEDS_NAME " Can't ioremap!\n");
return -ENOMEM;
}
printk (LEDS_NAME " initilized!\n");
return 0;
}
module_init (hello_init); //模塊入口
module_exit (hello_exit); //模塊出口
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("StephenYee([email protected])");
MODULE_DESCRIPTION ("S3C2410 leds driver!");
第二個(完整):
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#define DEVICE_NAME "leds" /* 加載模式後,執行”cat /proc/devices”命令看到的設備名稱 */
#define LED_MAJOR 231 /* 主設備號 */
/* 應用程序執行ioctl(fd, cmd, arg)時的第2個參數 */
#define IOCTL_LED_ON 0
#define IOCTL_LED_OFF 1
/* 用來指定LED所用的GPIO引腳 */
static unsigned long led_table [] = {
S3C2410_GPB5,
S3C2410_GPB6,
S3C2410_GPB7,
S3C2410_GPB8,
};
/* 用來指定GPIO引腳的功能:輸出 */
static unsigned int led_cfg_table [] = {
S3C2410_GPB5_OUTP,
S3C2410_GPB6_OUTP,
S3C2410_GPB7_OUTP,
S3C2410_GPB8_OUTP,
};
/* 應用程序對設備文件/dev/leds執行open(...)時,
* 就會調用s3c24xx_leds_open函數
*/
static int s3c24xx_leds_open(struct inode *inode, struct file *file)
{
int i;
for (i = 0; i < 4; i++) {
// 設置GPIO引腳的功能:本驅動中LED所涉及的GPIO引腳設爲輸出功能
s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
}
return 0;
}
/* 應用程序對設備文件/dev/leds執行ioclt(...)時,
* 就會調用s3c24xx_leds_ioctl函數
*/
static int s3c24xx_leds_ioctl(
struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
if (arg > 4) {
return -EINVAL;
}
switch(cmd) {
case IOCTL_LED_ON:
// 設置指定引腳的輸出電平爲0
s3c2410_gpio_setpin(led_table[arg], 0);
return 0;
case IOCTL_LED_OFF:
// 設置指定引腳的輸出電平爲1
s3c2410_gpio_setpin(led_table[arg], 1);
return 0;
default:
return -EINVAL;
}
}
/* 這個結構是字符設備驅動程序的核心
* 當應用程序操作設備文件時所調用的open、read、write等函數,
* 最終會調用這個結構中指定的對應函數
*/
static struct file_operations s3c24xx_leds_fops = {
.owner = THIS_MODULE, /* 這是一個宏,推向編譯模塊時自動創建的__this_module變量 */
.open = s3c24xx_leds_open,
.ioctl = s3c24xx_leds_ioctl,
};
/*
* 執行insmod命令時就會調用這個函數
*/
static int __init s3c24xx_leds_init(void)
{
int ret;
/* 註冊字符設備
* 參數爲主設備號、設備名字、file_operations結構;
* 這樣,主設備號就和具體的file_operations結構聯繫起來了,
* 操作主設備爲LED_MAJOR的設備文件時,就會調用s3c24xx_leds_fops中的相關成員函數
* LED_MAJOR可以設爲0,表示由內核自動分配主設備號
*/
ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &s3c24xx_leds_fops);
if (ret < 0) {
printk(DEVICE_NAME " can't register major number\n");
return ret;
}
printk(DEVICE_NAME " initialized\n");
return 0;
}
/*
* 執行rmmod命令時就會調用這個函數
*/
static void __exit s3c24xx_leds_exit(void)
{
/* 卸載驅動程序 */
unregister_chrdev(LED_MAJOR, DEVICE_NAME);
}
/* 這兩行指定驅動程序的初始化函數和卸載函數 */
module_init(s3c24xx_leds_init);
module_exit(s3c24xx_leds_exit);
/* 描述驅動程序的一些信息,不是必須的 */
MODULE_AUTHOR("http://www.100ask.net");
MODULE_DESCRIPTION("S3C2410/S3C2440 LED Driver");
MODULE_LICENSE("GPL");
第三個(完整):
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#define CMD_LED_ON 1
#define CMD_LED_OFF 0
#define CMD_LED_GET_STATE 2 //獲得LED燈狀態
#define LEDS_NUM 4 //LED燈數量
#define S3C24XX_LEDS_PHY_BASE 0x56000050 //控制寄存器物理地址
#define GPFCON_OFFSET 0x0
#define GPFDAT_OFFSET 0x1
#define GPFUP_OFFSET 0x2
static int major = 0; //主設備號
module_param(major, int, S_IRUGO|S_IWUGO); //模塊參數
MODULE_PARM_DESC(major,"s3c24xx leds major number"); //參數描述
struct s3c24xx_leds_dev { //設備結構體
unsigned char led_state; //led_state [4-7], 1: on, 0: off
unsigned long *pBase;
//...
};
struct s3c24xx_leds_dev *devs =NULL; //定義一個設備結構體對象
static int s3c24xx_leds_open (struct inode *inode, struct file *filp) //打開函數
{
unsigned long * pReg = devs->pBase;
filp->private_data = (void *)devs; //獲得設備結構體對象信息
//配置寄存器
//GPFCON output.
writel( (readl(pReg +GPFCON_OFFSET) &~0xff00) |0x5500, pReg +GPFCON_OFFSET);
//GPFUP disable pullup.
writel( (readl(pReg +GPFUP_OFFSET) &~0xff00) |0x5500, pReg +GPFUP_OFFSET);
return 0;
}
static int s3c24xx_leds_close (struct inode *inode, struct file *filp) //關閉函數
{
filp->private_data = NULL;
return 0;
}
static ssize_t s3c24xx_leds_read (struct file * filp, char __user *buf, size_t len, loff_t *loff)
{
struct s3c24xx_leds_dev *pdev =(struct s3c24xx_leds_dev *)filp->private_data; //獲得結構體信息
unsigned char kbuff;
kbuff = pdev->led_state >>4; //右移四位(定位引腳)
//完成內核空間到用戶空間的拷貝
//static inline int copy_to_user(void __user *to, const void *from, int n)
if (copy_to_user((void __user *)buf, &kbuff, 1) )
{
return -EFAULT;
}
return 0;
}
static ssize_t s3c24xx_leds_write (struct file * filp, char __user *buf, size_t len, loff_t *loff)
{
struct s3c24xx_leds_dev *pdev =(struct s3c24xx_leds_dev *)filp->private_data;
unsigned char kbuff;
//完成用戶空間到內核空間的拷貝
//static inline int copy_from_user(void *to, const void __user *from, int n)
if (copy_from_user(&kbuff, (void __user *)buf, 1))
{
return -EFAULT;
}
kbuff = kbuff<<4; //獲得數據
//真正的LED操作
if ( pdev->led_state != kbuff ) {
//...
int i;
for (i = 0; i< LEDS_NUM;i++) {
if( (pdev->led_state & (1<< (4+i))) ^ (kbuff & (1<<( 4+i))) ) {
if (pdev->led_state & (1<< (4+i)) ) { // 原來的狀態是開,現在設置爲關
writel( (readl(pdev->pBase +GPFDAT_OFFSET) &~0xf0) | (1<< ( 4+i )), pdev->pBase+GPFDAT_OFFSET);
} else { // 原來的狀態是關,現在設置爲開
writel( ((readl(pdev->pBase +GPFDAT_OFFSET) &~0xf0)) | ~(1<< ( 4+i)) & 0xf0, pdev->pBase+GPFDAT_OFFSET);
}
}
}
}
pdev->led_state = kbuff;
return 0;
}
static int s3c24xx_leds_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
struct s3c24xx_leds_dev *pdev =(struct s3c24xx_leds_dev *)filp->private_data;
unsigned char state;
//參數正確性驗證
if (arg <1 || arg >4)
return -EINVAL;
//權限管理
if (!capable (CAP_SYS_ADMIN)) {
return -EPERM;
}
switch (cmd) {
case CMD_LED_ON: //開
//...
// writel( readl(pdev->pBase +GPFDAT_OFFSET) &~0xf0 |0xe0, pdev->pBase+GPFDAT_OFFSET);
writel( readl(pdev->pBase +GPFDAT_OFFSET) &~0xf0 | ~(1<< ( 4+arg -1)) & 0xf0, pdev->pBase+GPFDAT_OFFSET);
pdev->led_state |= 1<< ( 4+arg -1);
break;
case CMD_LED_OFF: //關
//writel( readl(pdev->pBase +GPFDAT_OFFSET) &~0xf0 |0xf0, pdev->pBase+GPFDAT_OFFSET);
writel( readl(pdev->pBase +GPFDAT_OFFSET) | 1<< ( 4+arg -1), pdev->pBase+GPFDAT_OFFSET);
pdev->led_state &= ~(1<< ( 4+arg -1));
break;
case CMD_LED_GET_STATE:
state = pdev->led_state >>4;
printk("driver: led_sate:%0x", state& 0x0f );
return copy_to_user((void *) arg, &state, 1) ? -EFAULT : 0;
break;
default:
printk("Unknown command\n");
break;
}
return 0;
}
static struct file_operations s3c24xx_leds_ops = {
.owner = THIS_MODULE,
.open = s3c24xx_leds_open,
.release = s3c24xx_leds_close,
.read = s3c24xx_leds_read,
.write = s3c24xx_leds_write,
.ioctl = s3c24xx_leds_ioctl,
//...
};
static int __init s3c24xx_leds_init(void)
{
dev_t dev_no = MKDEV(major, 0);
int itmp = -1, ret = -1;
unsigned long *pmem = NULL;
devs = kmalloc(sizeof(struct s3c24xx_leds_dev), GFP_KERNEL); //分配內存
if (!devs) {
ret = -ENOMEM;
goto out;
}
itmp = register_chrdev(dev_no, "leds", &s3c24xx_leds_ops); //註冊
if(itmp < 0) {
ret = -ENODEV;
goto out1;
}
if( itmp > 0 )
major = itmp;
printk("major = %d\n", major);
//地址映射
pmem = ioremap(S3C24XX_LEDS_PHY_BASE, 12);
if (!pmem) {
ret = -ENOMEM;
goto out2;
}
devs->pBase = pmem; //地址關聯
return 0;
out2:
unregister_chrdev(dev_no, "leds");
out1:
kfree(devs);
out:
return ret;
}
static void __exit s3c24xx_leds_exit(void)
{
dev_t dev_no = MKDEV(major, 0);
iounmap(devs->pBase);
unregister_chrdev(dev_no, "leds");
kfree(devs);
devs = NULL;
}
module_init(s3c24xx_leds_init);
module_exit(s3c24xx_leds_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("StephenYee([email protected])");
應用程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#define LEDS_NAME "/dev/leds"
#define CMD_LED_ON 1
#define CMD_LED_OFF 0
#define CMD_LED_GET_STATE 2
#define LEDS_NUM 4
int main (int argc, char **argv)
{
int fd = -1, ret= -1;
int i;
int leds;
unsigned char buf;
fd = open (LEDS_NAME, O_RDWR);
if (fd < 0) {
printf ("Can't open %s\n", LEDS_NAME);
return -1;
}
//讀取當前LED狀態
ret = read(fd, &buf, 1);
if (ret < 0) {
printf ("Can't read %s\n", LEDS_NAME);
}
for (i = 0; i< LEDS_NUM; i++) {
printf( "LED%d state is:%s\n", i+1, buf & (1<<i)? "On" :"Off");
}
while (1) {
//通過IOCTL循環點D11
ioctl (fd, CMD_LED_ON, 2);
ret = read(fd, &buf, 1);
for (i = 0; i< LEDS_NUM; i++) {
printf( "1: LED%d state is:%s\n", i+1, buf & (1<<i)? "On" :"Off");
}
sleep (1);
ioctl (fd, CMD_LED_OFF, 2);
ret = read(fd, &buf, 1);
for (i = 0; i< LEDS_NUM; i++) {
printf( "2: LED%d state is:%s\n", i+1, buf & (1<<i)? "On" :"Off");
}
sleep (1);
ioctl (fd, CMD_LED_ON, 3);
// Get state
ret = ioctl (fd, CMD_LED_GET_STATE, &leds);
leds &= 0x0f;
printf("ret = %d, leds =%d\n",ret, leds);
for (i = 0; i< LEDS_NUM; i++) {
printf( " 3: LED%d state is:%s\n", i+1, leds & (1<<i)? "On" :"Off");
}
sleep(1);
//設置狀態:
buf ^= (1<<3); //改變D11的狀態
write(fd, &buf, 1);
sleep(1);
//設置狀態:
buf ^= (1<<1); //改變D11的狀態
write(fd, &buf, 1);
}
sleep(1);
return 0;
}