📄 s3c2440_gpio.c
字号:
#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/miscdevice.h>#include <linux/sched.h>#include <linux/delay.h>#include <linux/poll.h>#include <linux/spinlock.h>#include <linux/irq.h>#include <linux/delay.h> #include <asm/hardware.h>#include <stelcom/stel_proc.h> //定义/proc下模块版本信息相关变量#define DEBUG#ifdef DEBUG#define DBG(arg... ) printk( "<gpio "__FUNCTION__ "()>\t" ##arg )#else#define DBG(args...)#endif#define DRIVER_VERSION "0.02"#define DRIVER_AUTHOR "Siki <shi@162.com>"#define STELCOM_DEV_DIR "ee100"#define DRIVER_DESC " GPIO control Driver"#define DEVICE_NAME "gpio"#define DEVICE_MAJOR 0#ifdef CONFIG_PROC_MODVERSIONstatic int stel_proc_read(char *buf, char **start, off_t off, int count, int *eof, void *data);static struct stelproc_entry stel = { "stel_gpio", stel_proc_read , DRIVER_VERSION};static char proc_name[50];#endif /* CONFIG_PROC_MODVERSION *//* ioctl number, NR *//* ioctl contain 4 bytes.The content from lower to higer bit is: * Nr(8 bits), Type(8 bits), Size(14 bits), Direction(2 bits) for arm architeture * In this source, Nr decides the GPIO to be controled in ioctl,Type=GPIO_IOC_MAGIC, * Size=1, Direction shows whether to read data from userspace or to write data * to userspace. * Nr must only be one of below: * GPA0~GPA24:0-24 * GPB0~GPB15:25-40 * GPC0~GPC15:41-56 * GPD0~GPD15:57-72 * GPE0~GPE15:73-88 * GPF0~GPF15:89-104 * GPG0~GPG15:105-120 * GPH0~GPH15:121-136 * GPJ0~GPJ15:137-152 * * Attention: change the GPIO output may result in crash of the system.Be careful!!!! */#define GPIO_IOC_MAGIC 0xB4#define GPIO_IOC_MAXNR 152static int gpio_major, gpio_opened = 0;static devfs_handle_t devfs_handle, devfs_dir_handle;static int gpio_open(struct inode *inode, struct file *file){ DBG("Open the gpio driver...\n"); MOD_INC_USE_COUNT; gpio_opened = 1; return 0;}static int gpio_close(struct inode *inode, struct file *filp){ DBG("Close the gpio driver...\n"); if( gpio_opened == 0 ) { printk("The gpio driver hasn't opened!\n"); return 0; } MOD_DEC_USE_COUNT; gpio_opened = 0; return 0;}static int gpio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ int err = 0, dir, tmp, port, nr;//port and nr to specify the GPIO.for example, GPA2:port=0,nr=2 u32 tmp2, value = 0; char from, to; int retval = 0; /* * extract the type and number bitfields, and don't decode * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() */ if (_IOC_TYPE(cmd) != GPIO_IOC_MAGIC) return -ENOTTY; if (_IOC_NR(cmd) > GPIO_IOC_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 *)arg, _IOC_SIZE(cmd)); else if (_IOC_DIR(cmd) & _IOC_WRITE) err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)); if (err) return -EFAULT; dir = _IOC_DIR(cmd); tmp = _IOC_NR(cmd); if(tmp < 25) { port = 0; nr = tmp; } else { port = (tmp-25)/16 + 1; nr = (tmp-25) % 16; } DBG("cmd is 0x%x.\n",cmd); DBG("dir is %d.\n",dir); DBG("port is %d.\n",port); DBG("nr is %d.\n",nr); switch(dir) { case _IOC_READ: DBG("Enter the IOC_READ.\n"); if(port == 0) { //GPA DBG("sikinzen1.\n"); GPACON &= (0xffffffff^(0x00000001<<nr)); DBG("sikinzen2.\n"); value = GPADAT & (0x00000001<<nr); DBG("sikinzen3.\n"); value >>= nr; DBG("GPA:value is 0x%x.\n", value); } else if(port == 8) { //GPJ tmp2 = GPJCON; tmp2 |= (0x00000003<<(2*nr)); tmp2 &= (0xffffffff^(0x00000002<<(2*nr))); GPJCON = tmp2; value = GPJDAT & (0x00000001<<nr); value >>= nr; DBG("GPJ:value is 0x%x.\n", value); } else { //GPB ~ GPH tmp = port*0x10; tmp2 = bGPIO(tmp); tmp2 |= (0x00000003<<(2*nr)); tmp2 &= (0xffffffff^(0x00000002<<(2*nr))); bGPIO(tmp) = tmp2; value = bGPIO(tmp+4) & (0x00000001<<nr); value >>= nr; DBG("GPB ~ GPH:value is 0x%x.\n", value); } from = value; DBG("Data read is %d.\n", from); put_user(from, (char *)arg); break; case _IOC_WRITE: DBG("Enter the IOC_WRITE.\n"); get_user(to, (char *)arg); value = to; DBG("Data to be write is 0x%x.\n", value); if((value != 0) &&(value != 1)) { printk("The data write to I/O is wrong!\n"); return -1; } value <<= nr; DBG("Value is:0x%x.\n", value); if(port == 0) { //GPA DBG("sikinzen1.\n"); GPACON &= (0xffffffff^(0x00000001<<nr)); tmp2 = GPACON; DBG("GPACON:0x%x.\n", tmp2); tmp2 = GPADAT; tmp2 &= (0xffffffff^(0x00000001<<nr)); tmp2 |= value; GPADAT = tmp2; DBG("GPADAT:0x%x.\n", tmp2); } else if(port == 8) { //GPJ DBG("sikinzen1.\n"); tmp2 = GPJCON; tmp2 |= (0x00000003<<(2*nr)); tmp2 &= (0xffffffff^(0x00000002<<(2*nr))); GPJCON = tmp2; DBG("GPJCON:0x%x.\n", tmp2); tmp2 = GPJDAT; tmp2 &= (0xffffffff^(0x00000001<<nr)); tmp2 |= value; GPJDAT = tmp2; DBG("GPJDAT:0x%x.\n", tmp2); } else { //GPB ~ GPH tmp = port*0x10; tmp2 = bGPIO(tmp); tmp2 |= (0x00000003<<(2*nr)); tmp2 &= (0xffffffff^(0x00000002<<(2*nr))); bGPIO(tmp)= tmp2; DBG("GPBCON~GPHCON:0x%x.\n", tmp2); tmp2 = bGPIO(tmp+4); tmp2 &= (0xffffffff^(0x00000001<<nr)); tmp2 |= value; bGPIO(tmp+4) = tmp2; DBG("GPBDAT~GPHDAT:0x%x.\n", tmp2); tmp2 = bGPIO(tmp+8); tmp2 &= (0xffffffff^(0x00000001<<nr)); //tmp2 |= value; bGPIO(tmp+8) = tmp2; DBG("GPBUP~GPHUP:0x%x.\n", tmp2); } break; default: DBG("Users haven't specified the direction.\n"); return -ENOTTY; } return retval; }static struct file_operations gpio_fops = { owner: THIS_MODULE, open: gpio_open, release: gpio_close, ioctl: gpio_ioctl,};#ifdef CONFIG_PROC_MODVERSIONstatic int stel_proc_read(char *buf, char **start, off_t off, int count, int *eof, void *data){ int len = 0; len = sprintf(buf, "%s\n", stel.version); return len;}#endif /* CONFIG_PROC_MODVERSION */static int __init gpio_init(void){ int ret; // 注册为字符设备:主设备号为0时,由系统自动分配未使用的设备号 ret = register_chrdev(DEVICE_MAJOR, DEVICE_NAME, &gpio_fops); if(ret < 0) { printk(DEVICE_NAME " can't register major number\n"); return ret; } // 向设备文件系统注册该设备#ifdef CONFIG_DEVFS_FS gpio_major = ret; devfs_dir_handle = devfs_mk_dir(NULL,STELCOM_DEV_DIR, NULL); devfs_handle = devfs_register(devfs_dir_handle,DEVICE_NAME, DEVFS_FL_DEFAULT, gpio_major, 0, S_IFCHR | S_IRUSR | S_IWUSR, &gpio_fops, NULL);#endif#ifdef CONFIG_PROC_MODVERSION sprintf(proc_name, "%s/%s", PROC_MODVER_DIR,stel.name); create_proc_read_entry ( proc_name, 0, NULL, stel.fn, NULL);#endif printk(KERN_INFO DRIVER_DESC "v" DRIVER_VERSION " has been initialized.\n"); return 0;}static void __exit gpio_exit(void){#if CONFIG_PROC_MODVERSION remove_proc_entry ( proc_name, NULL);#endif devfs_unregister(devfs_handle); // 从设备文件系统注销该设备 unregister_chrdev(gpio_major, DEVICE_NAME); // 从字符设备注销该设备 printk(KERN_INFO"The GPIO driver has been unregistered!\n");}module_init(gpio_init);module_exit(gpio_exit);MODULE_AUTHOR( DRIVER_AUTHOR );MODULE_DESCRIPTION( DRIVER_DESC );MODULE_LICENSE("GPL");EXPORT_NO_SYMBOLS;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -