/*
* main.c -- the bare scull char module
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/seq_file.h>
#include <linux/cdev.h>
#include <linux/sched.h> /* current and everything */
#include <linux/spinlock.h> /* spinlock_t */ // dw
#include <asm/system.h> /* cli(), *_flags */
#include <asm/uaccess.h> /* copy_*_user */
#include <asm/atomic.h> /* atomic_t */
#include "IO_mem.h" /* local definitions */
/*
* Our parameters which can be set at load time.
*/
int IO_mem_major = 0;
int IO_mem_minor = 0;
unsigned int gpjcon_old =0;
unsigned int gpjdat_old =0;
unsigned int gpjup_old =0;
unsigned long io_addr;
module_param(IO_mem_major, int, S_IRUGO);
module_param(IO_mem_minor, int, S_IRUGO);
struct IO_mem_dev *IO_mem_devices; /* allocated in scull_init_module */
static atomic_t IO_mem_available = ATOMIC_INIT(1); // #define ATOMIC_INIT(i) { (i) } //用于在定义原子变量时,初始化为指定的值。
static spinlock_t IO_mem_lock = SPIN_LOCK_UNLOCKED; // 自旋锁初始化
static DECLARE_WAIT_QUEUE_HEAD(IO_mem_wait); // 生成一个等待队列头,名字为IO_men_wait
struct resource *IO_mem_resource; // 用来映射物理地址
/*
* Open and close
*/
int IO_mem_open(struct inode *inode, struct file *filp)
{
struct IO_mem_dev *dev; /* device information */
spin_lock(&IO_mem_lock); // 进入临界区前,获得所需要的锁
while (! atomic_dec_and_test (&IO_mem_available)) {
atomic_inc(&IO_mem_available);
spin_unlock(&IO_mem_lock);
if (filp->f_flags & O_NONBLOCK) return -EAGAIN;
if (wait_event_interruptible (IO_mem_wait, atomic_read (&IO_mem_available)))
return -ERESTARTSYS; /* tell the fs layer to handle it */
spin_lock(&IO_mem_lock);
} // 原子操作 todo 《ldd3》P133
spin_unlock(&IO_mem_lock);
dev = container_of(inode->i_cdev, struct IO_mem_dev, cdev);
io_addr =(unsigned long) ioremap_nocache(0x56000010 , 0x0c); //GPBCON 0x56000010 《s3c240 data》
printk( "io_addr : %lx\n", io_addr);
/* now trim to 0 the length of the device if open was write-only */
gpjcon_old = ioread32 (io_addr); // 从I/O内存中读取
gpjdat_old = ioread32 (io_addr+4);
gpjup_old = ioread32 (io_addr+8);
mb(); //加入内存屏障 保证上面的操作完成
iowrite32(0x55555555,io_addr); // 设置端口为输出模式
iowrite32(0x1fff,io_addr+8); // 关闭上拉功能
wmb(); //why 上面的iowrite32() 和iowrite32(0x0,io_addr+4) 隔开
iowrite32(0x00f0,io_addr+4); // 初始电平为高,关闭LED灯
filp->private_data = dev; /* for other methods */
return nonseekable_open(inode, filp); /* success */ //设备不支持 llseek
}
int IO_mem_release(struct inode *inode, struct file *filp)
{
iowrite32((u32) gpjcon_old ,io_addr); // IO内存释放为什么这么表示?
iowrite32((u32) gpjdat_old ,io_addr+4);
iowrite32((u32) gpjup_old ,io_addr+8);
iounmap((void *)io_addr); // 解除映射
// ioport_unmap((void *)io_addr); /*WARNING: "ioport_unmap" undefined! Don't use it*/
atomic_inc(&IO_mem_available); /* release the device */
wake_up_interruptible_sync(&IO_mem_wait); /* awake other uid's */
return 0;
}
/*
* The ioctl() implementation
*/
int IO_mem_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
int err = 0;
int retval = 0;
unsigned int current_status;
/*
* extract the type and number bitfields, and don't decode
* wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
*/
if (_IOC_TYPE(cmd) != IO_MEM_MAGIC) return -ENOTTY;
if (_IOC_NR(cmd) > IO_MEM_MAXNR) return -ENOTTY;
/*
* the direction is a bitmask, and VERIFY_WRITE catches R/W
* transfers. `Type' is user-oriented, while
* access_ok is kernel-oriented, so the concept of "read" and
* "write" is reversed
*/
if (_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); // arg 用户空间地址
if (err) return -EFAULT;
switch(cmd) {
case IO_MEM_0:
iowrite32(0x0,io_addr+4); // 低电平LED亮
break;
case IO_MEM_1:
iowrite32(0x0030,io_addr+4); // 亮7,8
break;
case IO_MEM_2:
iowrite32(0x00c0,io_addr+4); // 亮4,5
break;
case IO_MEM_3:
iowrite32(0x0800,io_addr+4); // 亮 4,5,6
break;
case IO_MEM_STATUS:
current_status = ioread32 (io_addr+4);
retval = __put_user(current_status, (unsigned int __user *)arg);
break;
default: /* redundant, as cmd was checked against MAXNR */
return -ENOTTY;
}
return retval;
}
struct file_operations IO_mem_fops = {
.owner = THIS_MODULE,
.ioctl = IO_mem_ioctl,
.open = IO_mem_open,
.release = IO_mem_release,
.llseek = no_llseek,
};
/*
* Finally, the module stuff
*/
/*
* The cleanup function is used to handle initialization failures as well.
* Thefore, it must be careful to work correctly even if some of the items
* have not been initialized
*/
void IO_mem_cleanup_module(void)
{
dev_t devno = MKDEV(IO_mem_major, IO_mem_minor);
if (IO_mem_resource!=NULL) release_mem_region(0x56000010, 0x0c);
/* Get rid of our char dev entries */
if (IO_mem_devices) {
cdev_del(&IO_mem_devices->cdev);
kfree(IO_mem_devices);
}
/* cleanup_module is never called if registering failed */
unregister_chrdev_region(devno, 1);
}
/*
* Set up the char_dev structure for this device.
*/
static void IO_mem_setup_cdev(struct IO_mem_dev *dev)
{
int err, devno = MKDEV(IO_mem_major, IO_mem_minor);
cdev_init(&dev->cdev, &IO_mem_fops);
dev->cdev.owner = THIS_MODULE;
err = cdev_add (&dev->cdev, devno, 1);
/* Fail gracefully if need be */
if (err)
printk(KERN_NOTICE "Error %d adding IO_mem", err);
}
int IO_mem_init_module(void)
{
int result;
dev_t dev = 0;
/*
* Get a range of minor numbers to work with, asking for a dynamic
* major unless directed otherwise at load time.
*/
if (IO_mem_major) {
dev = MKDEV(IO_mem_major, IO_mem_minor);
result = register_chrdev_region(dev, 1, "IO_mem");
} else {
result = alloc_chrdev_region(&dev, IO_mem_minor, 1,
"IO_mem");
IO_mem_major = MAJOR(dev);
}
if (result < 0) {
printk(KERN_WARNING "IO_mem: can't get major %d\n", IO_mem_major);
return result;
}
/*
* allocate the devices -- we can't have them static, as the number
* can be specified at load time
*/
IO_mem_devices = kmalloc(sizeof(struct IO_mem_dev), GFP_KERNEL);
if (!IO_mem_devices) {
result = -ENOMEM;
goto fail; /* Make this more graceful */
}
memset(IO_mem_devices, 0, sizeof(struct IO_mem_dev));
/* Initialize each device. */
init_MUTEX(&IO_mem_devices->sem);
IO_mem_setup_cdev(IO_mem_devices);
if ((IO_mem_resource=request_mem_region(0x56000010, 0x0c,"IO_mem"))==NULL)
goto fail;
// #define request_mem_region(start, n,name) __request_region(&iomem_resource, (start), (n), (name))
return 0; /* succeed */
fail:
IO_mem_cleanup_module();
return result;
}
module_init(IO_mem_init_module);
module_exit(IO_mem_cleanup_module);
MODULE_AUTHOR("dengwei");
MODULE_LICENSE("Dual BSD/GPL");
/**************************************
GPJCON 0x560000D0 Port J control
应该可以用来驱动LED 因为是对I/O口的控制
GPBCON 0x56000010 [17:10]
GPBDAT 0x56000014
GPBUP 0x56000018
nLED1 GPB5
nLED2 GPB6
nLED3 GPB7
nLED4 GPB8
GPBDAT [10:0] GPB10~GPB0
************************************/
|