/* In the following, we implement a linux character driver as described in [LDD, Chapter 3]. First, connect GPIO 130 and GPIO 134 on the devkit add-on board using the diagrams found at http://devkit8000addon.wikispaces.com/Interfaces and http://devkit8000addon.wikispaces.com/PCB. In VMware, open 3 terminals. In terminal 1, establish a connection to devkit using > ssh root@10.9.8.2 In terminal 2, do the same > ssh root@10.9.8.2 In terminal 3, compile the module and transfer it to devkit > cat > Makefile obj-m := mygpioDOC.o (CTRL-C) > make -C /home/stud/source/linux-2.6.28-omap/ M=`pwd` modules > scp mygpioDOC.ko root@10.9.8.2:/home/root/ Back to terminal 1, insert the module into the kernel and check that no error occurred > insmod mygpioDOC.ko > dmesg Create two nodes, one for input, GPIO 130, and one for output, GPIO 134. > mknod gpio0 c 62 0 > mknod gpio4 c 62 4 In terminal 2, read data from GPIO 130 using > cat /dev/gpio0 NB: this is not exactly the right way to read from the GPIO but it is enough for our purposes In terminal 1, set GPIO 134 to 0 or 1 (low/high) using > echo 0 > /dev/gpio4 > echo 1 > /dev/gpio4 > etc. In terminal 2, sequences of 0s or 1s are output depending on the value written to GPIO 134. Use CTRL-C to end the output. Finally, remove the module from the kernel by writing in terminal 1 or 2 > rmmod mygpioDOC.ko > dmesg */ /* Bibliography: [LDD] Linux Device Drivers, 3rd Ed. */ #include <linux/gpio.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> #include <asm/uaccess.h> #include <linux/types.h> /* The major number MYGPIO_MAJOR identifies our driver and the minor numbers MYGPIO_MINOR, MYGPIO_MINOR+1,..., MYGPIO_MINOR + MYGPIO_CHS -1 identify each of the eight GPIOs. */ #define MYGPIO_MAJOR 62 #define MYGPIO_MINOR 0 #define MYGPIO_CHS 8 /* The eight GPIO's are accessed through character device files gpio0,...,gpio7 that must be created in the /dev directory. > sudo mknod /dev/gpio0 c 62 0 > sudo mknod /dev/gpio1 c 62 1 ... > sudo mknod /dev/gpio7 c 62 7 */ #define MAXLEN 512 #define MYGPIO_MIN_IN 0 #define MYGPIO_MAX_IN 3 #define MYGPIO_MIN_OUT 4 #define MYGPIO_MAX_OUT 7 /* Char DEVices are represented in the kernel by means of a cdev structure defined in linux/cdev.h, c.f. [LDD, p. 55]. This structure must be initialized in the init() function below */ static struct cdev MyGpioDev; /* Each field in the file_operations structure points to a function supported by the device. The eight GPIO character devices support as a minimum the 4 file operations open(), release(), read() and write(), which are implemented below. */ struct file_operations mygpio_Fops; static int devno; /* The gpioPort structure relates the 8 GPIOs with their respective MINOR number and I/O direction. */ struct gpioPort{ unsigned int num; enum direnu { in, out } dir; const char label[10]; // Very important to specify a size. } gpio[] = { {130, in, "gpio0_0"}, //Read only {131, in, "gpio0_1"}, //Read only {132, in, "gpio0_2"}, //Read only {133, in, "gpio0_3"}, //Read only {134, out, "gpio0_4"}, //Write only {135, out, "gpio0_5"}, //Write only {136, out, "gpio0_6"}, //Write only {137, out, "gpio0_7"} //Write only }; /*****************************************************************/ /*************************** INIT ********************************/ /*****************************************************************/ static int __init mygpio_init(void) { /* The purpose of the init() function is to register the functionalities implemented by our module. */ int err, i; printk(KERN_ALERT "Mygpio Module Inserted\n"); /* The kernel uses the dev_t type (linux/types.h) to hold the major and minor numbers of a device. The MKDEV macro constructs the dev_t number of a device given its major and minor numbers. */ devno = MKDEV(MYGPIO_MAJOR, MYGPIO_MINOR); /* The device numbers that we wish to work with are allocated using the register_chrdev_region() function. Here, we allocate device numbers devno,...,devno + MYGPIO_CHS -1, or in other terms, major number 62 and minor numbers 0,...,7. The name, "myGpio", associated with this device range will appear in /proc/devices, c.f. [LDD, p. 45]. */ if((err=register_chrdev_region(devno, MYGPIO_CHS , "myGpio"))<0){ printk(KERN_ALERT "Can't Register Major no: %i\n", MYGPIO_MAJOR); return err; } /* The cdev structure used by the kernel to represent the device must be initialized with the supported file operations, open(), release(), read() and write(). Pointers to those file operations are contained in the mygpio_Fops structure. The owner field should be set manually to THIS_MODULE, c.f. [LDD, p. 56] */ cdev_init(&MyGpioDev, &mygpio_Fops); MyGpioDev.owner = THIS_MODULE; /* Each of the 8 GPIOs are now requested explicitly in order to ensure that they have not already been claimed in another module. According to Documentation/gpio.txt, the call to gpio_request is optional. It gives us information on whether the GPIOs can be accessed without any conflicts with other drivers present on the system. */ for(i=0; i< MYGPIO_CHS; ++i) { if ( (err=gpio_request(gpio[i].num, gpio[i].label))<0){ printk(KERN_ALERT "Error %d requesting GPIO %d.\n",err,i); gpio_free(gpio[i].num); goto fail_init; } } /* The first four GPIOs will be used for input, i.e. they do not support the write operation. The last four GPIOs are only used for output operations, i.e. they do not support the read operation. */ for(i=0; i< MYGPIO_CHS; ++i) { if ( (gpio[i].dir == in) && (err=gpio_direction_input(gpio[i].num))<0 ) { printk(KERN_ALERT "Error %d marking direction on GPIO %d.\n",err,i); goto fail_dir; } else if ( (gpio[i].dir == out) && !(err=gpio_direction_output(gpio[i].num,0))<0 ){ printk(KERN_ALERT "Error %d marking direction on GPIO %d.\n",err,i); goto fail_dir; } } /* We are now ready to handle operations on the devices. The kernel is informed of the MyGpioDev structure. If the call to cdev_add() fails, the device has not been added to the system. If the call succeeds, the operations registered above with cdev_init() can now be called by the kernel. Therefore, the call to cdev_add should only be made after the character device and the GPIOs have been properly initialized with cdev_init and gpio_direction_* */ err = cdev_add(&MyGpioDev, devno, MYGPIO_CHS ); if (err) { printk (KERN_ALERT "Error %d adding MyGpio device\n", err); goto fail_add; } //No errors.. return 0; fail_add: fail_dir: for(i=0; i< MYGPIO_CHS; ++i) { gpio_free(gpio[i].num); } fail_init: cdev_del(&MyGpioDev); unregister_chrdev_region(devno, MYGPIO_CHS ); return err; } /*****************************************************************/ /*************************** EXIT ********************************/ /*****************************************************************/ static void mygpio_exit(void) { int err,i; /* The purpose of the exit() function is to release the resources that were previously allocated in the init() function */ printk(KERN_ALERT "Removing Mygpio Module\n"); /* The eight GPIOs are reset to their original (input) state */ for(i=0; i < MYGPIO_CHS; ++i) { if ( (err=gpio_direction_input(gpio[i].num)) <0 ) { printk(KERN_ALERT "Error %d reseting GPIO %d.\n",err,i); } } /* The eight GPIOs are released. Other device drivers are now allowed to request them. */ for(i=0; i< MYGPIO_CHS; ++i) { gpio_free(gpio[i].num); } /* The char device previously added to the system by calling cdev_init()/cdev_add() is now removed from the system. */ cdev_del(&MyGpioDev); /* The device numbers requested by the call to register_chrdev_region() are freed with */ unregister_chrdev_region(devno, MYGPIO_CHS ); } /*****************************************************************/ /*************************** OPEN ********************************/ /*****************************************************************/ int mygpio_open(struct inode *inode, struct file *filep) { int major, minor; /* Fetch the actual device number from the unique inode structure that is internally used by the kernel to represent the device file */ major = MAJOR(inode->i_rdev); minor = MINOR(inode->i_rdev); /* Note that [LDD] recommends the use of the two macros - unsigned int iminor(struct inode *inode), - unsigned int imajor(struct inode *inode), instead of MAJOR and MINOR. */ printk("Opening MyGpio Device [major], [minor]: %i, %i\n", major, minor); /* As a safety measure, we check that the major and minor numbers of the character device, which was opened for IO, actually corresponds to one of the eight GPIOs. If not, something is wrong! */ if ( major != MYGPIO_MAJOR ) { printk(KERN_ALERT "Invalid major device number.\n"); return -EFAULT; } //if minor < 0 or minor > 7, the minor number is invalid if ( minor < MYGPIO_MINOR || minor >= MYGPIO_MINOR + MYGPIO_CHS ) { printk(KERN_ALERT "Invalid minor device number.\n"); return -EFAULT; } /* Whenever a user application or another module opens one of the GPIO devices for IO, it must hold a reference to this module. Otherwise, this module could be removed from the kernel (rmmod) while another module is asleep inside this one during a blocking read or write. According to http://www.kernel.org/pub/linux/kernel/people/rusty/modules/FAQ (Rusty Russel wrote the last version of module.h in which try_get_module() and put_module() are defined), the try_module_get() function fails "when the module is not ready to be entered (ie. still in init_module) or it is being removed. This prevents you from entering the module as it is being discarded (init might fail, or it's being removed)". */ if (!try_module_get(mygpio_Fops.owner)) return -ENODEV; return 0; } /*****************************************************************/ /*************************** RELEASE *****************************/ /*****************************************************************/ int mygpio_release(struct inode *inode, struct file *filep) { int minor, major; major = MAJOR(inode->i_rdev); minor = MINOR(inode->i_rdev); printk("Closing MyGpio Device [major], [minor]: %i, %i\n", major, minor); /* As a safety measure, we check that the major and minor numbers of the character device being closed actually corresponds to one of the eight GPIOs. */ if ( major != MYGPIO_MAJOR ) { printk(KERN_ALERT "Invalid major device number.\n"); return -EFAULT; } //if minor < 0 or minor > 7, the minor number is invalid if ( minor < MYGPIO_MINOR || minor >= MYGPIO_MINOR + MYGPIO_CHS ) { printk(KERN_ALERT "Invalid minor device number.\n"); return -EFAULT; } /* The reference to this module is put back. The danger of being blocked inside this module while it is destroyed is over. */ module_put(mygpio_Fops.owner); return 0; } /*****************************************************************/ /*************************** WRITE *******************************/ /*****************************************************************/ ssize_t mygpio_write(struct file *filep, const char __user *ubuf, size_t count, loff_t *f_pos) { int err = 0; int minor, len, value; char kbuf[MAXLEN]; /* * Retrieve minor number from the file structure, *filep. */ minor = MINOR(filep->f_dentry->d_inode->i_rdev); printk(KERN_ALERT "Writing to MyGpio [Minor] %i \n", minor); /* If the minor number if strictly less than 4 or strictly greater than 7, it does not represent a GPIO port meant for output. */ if ( minor < MYGPIO_MIN_OUT || minor > MYGPIO_MAX_OUT) { printk(KERN_ALERT "Invalid minor device number.\n"); return -EFAULT; } /* We now demonstrate how data can be transfered from user space (user application) to kernel space (this driver module). copy_from_user() returns the number of bytes that were NOT successfully copied. Hence, a return value equal to 0 means success. */ len = count < MAXLEN ? count : MAXLEN; // The destination buffer can only contain up to MAXLEN bytes. Avoid buffer overflows in kernel space, please! err = copy_from_user(kbuf, ubuf, len); if (err != 0) { printk(KERN_ALERT "copy_from_user failed with error %d.\n",err); goto fail; } /* NB: In the particular case of a GPIO device, the file pointer may be left unchanged after an IO operation. */ /* The bytes transfered from user space are converted into a string and then into an integer value. */ kbuf[len] = '\0'; printk("String from user: %s\n", kbuf); sscanf(kbuf,"%i", &value); value = value > 0 ? 1 : 0; /* Set the GPIO port to either 0 or 1... */ gpio_set_value(gpio[minor].num,value); /* Return the number of bytes copied from user space to kernel space. If copy_from_user failed, return -EFAULT. */ return len; fail: return -EFAULT; } /*****************************************************************/ /*************************** READ *******************************/ /*****************************************************************/ ssize_t mygpio_read(struct file *filep, char __user *buf, size_t count, loff_t *f_pos) { char readBuf[MAXLEN]; int len, result=-1, minor; int err; /* Retrieve the minor device number from *filep */ minor = MINOR(filep->f_dentry->d_inode->i_rdev); printk(KERN_ALERT "Reading from MyGpio [Minor] %i \n", minor); /* If the minor number is <0 or >3, it does not represent a GPIO port meant for input. */ if ( minor < MYGPIO_MIN_IN || minor > MYGPIO_MAX_IN ) { printk(KERN_ALERT "Invalid minor device number.\n"); return -EFAULT; } /* Read a single input byte from the GPIO port */ result = gpio_get_value(gpio[minor].num); len=sprintf(readBuf,"%i", result); len++; // To include null-termination /* Copy string result to User Space */ err = copy_to_user(buf, readBuf, len); if (err != 0) { printk(KERN_ALERT "copy_to_user failed with error %d.\n",err); goto fail; } return len; fail: return -EFAULT; } /* The file_operation structure used in init() is initialized as follows: */ struct file_operations mygpio_Fops = { .owner = THIS_MODULE, .open = mygpio_open, .release = mygpio_release, .write = mygpio_write, .read = mygpio_read, }; /* Each field represents a pointer to a functionality supported by the driver. The .owner field should be set to THIS_MODULE, c.f. [LDD, p. 53] */ module_init(mygpio_init); //... is invoked when the module is loaded into the kernel (insmod) module_exit(mygpio_exit); //... is invoked when the module is removed from the kernel (rmmod) MODULE_DESCRIPTION("My GPIO Module"); MODULE_AUTHOR("Jérémy"); MODULE_LICENSE("GPL");
GPIO Linux Device Driver + Interrupts (Part 1)
Abonner på:
Opslag (Atom)