/* * main.c -- the bare scull char module * * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet * Copyright (C) 2001 O'Reilly & Associates * * The source code in this file can be freely used, adapted, * and redistributed in source or binary form, so long as an * acknowledgment appears in derived source files. The citation * should list that the code comes from the book "Linux Device * Drivers" by Alessandro Rubini and Jonathan Corbet, published * by O'Reilly & Associates. No warranty is attached; * we cannot take responsibility for errors or fitness for use. * */
int IO_port_open(struct inode *inode, struct file *filp) { struct IO_port_dev *dev; /* device information */
spin_lock(&IO_port_lock);
while (! atomic_dec_and_test (&IO_port_available)) { atomic_inc(&IO_port_available); spin_unlock(&IO_port_lock); if (filp->f_flags & O_NONBLOCK) return -EAGAIN; if (wait_event_interruptible (IO_port_wait, atomic_read (&IO_port_available))) return -ERESTARTSYS; /* tell the fs layer to handle it */
spin_lock(&IO_port_lock); }
spin_unlock(&IO_port_lock);
dev = container_of(inode->i_cdev, struct IO_port_dev, cdev);
/* now trim to 0 the length of the device if open was write-only */ gpjcon_old = (unsigned int) inl((unsigned long)S3C2410_GPBCON); gpjdat_old = (unsigned int) inl((unsigned long)S3C2410_GPBDAT); gpjup_old = (unsigned int) inl((unsigned long)S3C2410_GPBUP);
mb();
outl(0x01555555,(unsigned long)S3C2410_GPBCON); // printk( "IO_port con : %x\n", inl(S3C2440_GPJCON)); outl(0x1fff,(unsigned long)S3C2410_GPBUP); // printk( "IO_port up : %x\n", inl(S3C2440_GPJUP)); mb();
// outl(0x0,(unsigned long)S3C2410_GPBDAT); filp->private_data = dev; /* for other methods */
int IO_port_release(struct inode *inode, struct file *filp) { outl(gpjcon_old,(unsigned long)S3C2410_GPBCON); outl(gpjup_old,(unsigned long)S3C2410_GPBUP); outl(gpjdat_old,(unsigned long)S3C2410_GPBDAT); atomic_inc(&IO_port_available); /* release the device */ wake_up_interruptible_sync(&IO_port_wait); /* awake other uid's */
return 0; }
/* * The ioctl() implementation */
int IO_port_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_PORT_MAGIC) return -ENOTTY; if (_IOC_NR(cmd) > IO_PORT_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)); if (err) return -EFAULT;
switch(cmd) {
case IO_PORT_0: outl(0x1de,(unsigned long)S3C2410_GPBDAT); break;
case IO_PORT_1: /* Set: arg points to the value */ outl(0x1be,(unsigned long)S3C2410_GPBDAT); break;
case IO_PORT_2: /* Tell: arg is the value */ outl(0x17e,(unsigned long)S3C2410_GPBDAT); break;
case IO_PORT_3: /* Get: arg is pointer to result */ outl(0xfe,(unsigned long)S3C2410_GPBDAT); break;
case IO_PORT_STATUS: /* eXchange: use arg as pointer */ current_status = (unsigned int) inl((unsigned long)S3C2410_GPBDAT); retval = __put_user(current_status, (unsigned int __user *)arg); break;
default: /* redundant, as cmd was checked against MAXNR */ return -ENOTTY; } return retval;
/* * 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_port_cleanup_module(void) { dev_t devno = MKDEV(IO_port_major, IO_port_minor);
if (IO_port_resource!=NULL) release_region((unsigned long)S3C2410_GPBCON, 0x0c);
/* Get rid of our char dev entries */ if (IO_port_devices) { cdev_del(&IO_port_devices->cdev); kfree(IO_port_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_port_setup_cdev(struct IO_port_dev *dev) { int err, devno = MKDEV(IO_port_major, IO_port_minor);
cdev_init(&dev->cdev, &IO_port_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_port", err); }
int IO_port_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_port_major) { dev = MKDEV(IO_port_major, IO_port_minor); result = register_chrdev_region(dev, 1, "IO_port"); } else { result = alloc_chrdev_region(&dev, IO_port_minor, 1, "IO_port"); IO_port_major = MAJOR(dev); } if (result < 0) { printk(KERN_WARNING "IO_port: can't get major %d\n", IO_port_major); return result; }
/* * allocate the devices -- we can't have them static, as the number * can be specified at load time */ IO_port_devices = kmalloc(sizeof(struct IO_port_dev), GFP_KERNEL); if (!IO_port_devices) { result = -ENOMEM; goto fail; /* Make this more graceful */ } memset(IO_port_devices, 0, sizeof(struct IO_port_dev));
/* Initialize each device. */ init_MUTEX(&IO_port_devices->sem); IO_port_setup_cdev(IO_port_devices); if ((IO_port_resource=request_region((unsigned long)S3C2410_GPBCON, 0x0c,"IO_port"))==NULL) goto fail;