I'm trying to make a kernel module, using a Raspberry PI 2, to simply control a led as part of a school project.
"echo 0 > /dev/gpio-driver" -> Led OFF
"echo 1 > /dev/gpio-driver" -> Led On
However, my code isn't 100% correct, because I load the module and the led is always on, when I do
"echo 0 > /dev/gpio-driver" the led turn off but after a few seconds turn on again.
I don't know where the error is so I posted what I have so far.
Thanks a lot,
Eddy
/*
* Kernel modules for Raspberry PI
* Creation/Analysis of kernel modules for Raspberry PI
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/gpio.h>
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("");
MODULE_DESCRIPTION ("Simple kernel module to controle an LED");
#define DEVICE_NAME "gpio-chrdev"
#define DEVICE_FILE_NAME "gpio-file"
#define CLASS "gpio-project"
#define MAX_FILE_SIZE 3
static int size; //Initialize to 0 in module_init
static char data[MAX_FILE_SIZE];
static int __init gpio_driver_init (void);
static void __exit gpio_driver_exit (void);
static int gpio_driver_open (struct inode *inode, struct file *filp);
static int gpio_driver_release (struct inode *inode, struct file *filp);
static ssize_t gpio_driver_read (struct file *filp, char __user * buf,
size_t count, loff_t * offset);
static ssize_t gpio_driver_write (struct file *filp, const char __user * buf,
size_t count, loff_t * offset);
static dev_t devnr;
//static struct device *device;
static struct cdev LED_cdev;
/* Notify udev from the module - first create a virtual device class */
static struct class *gpio_class;
static int deviceopen = 0;
module_init(gpio_driver_init);
module_exit(gpio_driver_exit);
static struct file_operations LED_fops = {
.owner = THIS_MODULE,
.read = gpio_driver_read,
.write = gpio_driver_write,
.open = gpio_driver_open,
.release = gpio_driver_release,
};
/* This function is called when this module is loaded into the kernel */
static int __init gpio_driver_init(void){
/* Device file creation */
/* Alocate device number */
/* The major number will be chosen dynamically and returned in dev
* Returns zero or a negative erro code */
if(alloc_chrdev_region(&devnr, 0, 1, DEVICE_FILE_NAME) < 0) {
printk("Device class can't be created\n");
return -1;
}
/* Create device class */
/* Create a new class for this device. It will be visible in /sys/class */
if ((gpio_class = class_create(THIS_MODULE, CLASS)) == NULL) {
printk("Device class can't be created\n");
goto classerror;
}
/* Inicialize device*/
cdev_init (&LED_cdev, &LED_fops);
/* Activate the device */
if ((cdev_add (&LED_cdev, devnr, 1)) == -1){
printk("Unable to register the device\n");
goto validerror;
}
/* Create device file */
/* Device info can be viewd under /sys/class/arcom-project */
if ((device_create (gpio_class, NULL, devnr, NULL, DEVICE_NAME)) == NULL){
printk("Can't create device file\n");
goto fileerror;
}
/*
* GPIO configurations
* ********************/
/* Check if GPIO is valid */
if(gpio_is_valid(4) == false){
printk("GPIO is not valid\n");
goto validerror;
}
/* GPIO 4 Request */
if(gpio_request(4, "gpio-4") < 0 ){
printk("Can't request GPIO 4\n");
goto gpio4requesterror;
}
/* Configure GPIO as output*/
gpio_direction_output(4, 0);
//gpio_export(4, false);
return 0;
validerror:
device_destroy(gpio_class, devnr);
gpio4requesterror:
gpio_free(4);
fileerror:
class_destroy(gpio_class);
classerror:
unregister_chrdev_region(devnr, 1);
return -1;
}
static int gpio_driver_open(struct inode *inode, struct file *filp)
{
if (deviceopen)
{
printk(KERN_WARNING "Already open\n");
return -EBUSY;
}
try_module_get(THIS_MODULE);
deviceopen = 1;
return 0;
}
int gpio_driver_release(struct inode *inode, struct file *filp)
{
deviceopen = 0;
module_put(THIS_MODULE);
return 0;
}
/* Read data out of the buffer */
static ssize_t gpio_driver_read(struct file *filp, char __user *buf, size_t count, loff_t * offset)
{
int i;
int last = *offset + count;
if (last > size)
{
last = size;
}
i = last - *offset;
if (i == 0)
{
return 0;
}
if (copy_to_user (buf, data + *offset, i))
{
return -EFAULT;
}
return i;
}
/* Write data to buffer */
static ssize_t gpio_driver_write(struct file *filp, const char __user *buf, size_t count, loff_t * offset)
{
int i, n;
int last = *offset + count;
if (last > MAX_FILE_SIZE)
last = MAX_FILE_SIZE;
i = last - *offset;
n = copy_from_user (data + *offset, buf, i);
if (n != 0)
{
pr_warn ("Could not copy %d bytes\n", n);
return -EFAULT;
}
*offset += i;
if (size < *offset) //keep data from previous accesses
size = *offset;
/* Setting the LED - 0 or 1*/
if (data[0] == '0'){
gpio_set_value(4,0);
}
else if (data[0] == '1'){
gpio_set_value(4,1);
}
else {
printk("Invalid input\n");
}
return i;
}
/*
static void my_timer_func(struct timer_list *unused){
led_status = ~led_status;
outb(led_status, 0x378);
my_timer.expires += blink_delay;
add_timer(&my_timer);
}
*/
void gpio_driver_exit(void){
//gpio_unexport(4);
gpio_free(4);
cdev_del(&LED_cdev);
device_destroy(gpio_class, devnr);
class_destroy(gpio_class);
unregister_chrdev_region(devnr, 1);
printk("Live long and prosper\n");
}
EDIT:
Hello,
I've redone the program, also the module w1-gpio was loaded and messing with the module I tried to implement, so after the changes in the code and remove the module everything worked fine
/*
* Kernel modules for Raspberry PI
* Creation/Analysis of kernel modules for Raspberry PI
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/gpio.h>
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("");
MODULE_DESCRIPTION ("Simple kernel module to controle an LED");
#define MAX_FILE_SIZE 3
static int size; //Initialize to 0 in module_init
//static char data[MAX_FILE_SIZE];
static int __init gpio_driver_init (void);
static void __exit gpio_driver_exit (void);
static int gpio_driver_open (struct inode *inode, struct file *filp);
static int gpio_driver_release (struct inode *inode, struct file *filp);
static ssize_t gpio_driver_read (struct file *filp, char __user * buf,
size_t count, loff_t * offset);
static ssize_t gpio_driver_write (struct file *filp, const char __user * buf,
size_t count, loff_t * offset);
static dev_t devnr;
static struct cdev LED_cdev;
/* Notify udev from the module - first create a virtual device class */
static struct class *gpio_class;
static int deviceopen = 0;
module_init(gpio_driver_init);
module_exit(gpio_driver_exit);
static struct file_operations LED_fops = {
.owner = THIS_MODULE,
.read = gpio_driver_read,
.write = gpio_driver_write,
.open = gpio_driver_open,
.release = gpio_driver_release,
};
/* This function is called when this module is loaded into the kernel */
static int __init gpio_driver_init(void){
/* Device file creation */
/* Alocate device number */
/* The major number will be chosen dynamically and returned in dev
* Returns zero or a negative erro code */
if((alloc_chrdev_region(&devnr, 0, 1, "project_dev")) < 0) {
printk("Device class can't be created\n");
goto err_alloc;
}
/* Create cdev structure */
cdev_init (&LED_cdev, &LED_fops);
/* Add character device to the system */
if ((cdev_add (&LED_cdev, devnr, 1)) == -1){
printk("Unable to register the device\n");
device_destroy(gpio_class, devnr);
}
/* Create device class */
/* Create a new class for this device. It will be visible in /sys/class */
if ((gpio_class = class_create(THIS_MODULE, "project_class")) == NULL) {
printk("Device class can't be created\n");
goto err_class;
}
/* Create device file */
/* Device info can be viewd under /sys/class/arcom-project */
if ((device_create (gpio_class, NULL, devnr, NULL, "project_device")) == NULL){
printk("Can't create device file\n");
goto err_device;
}
size = 0;
/* GPIO configuration */
/* Checking if GPIO is valid */
if(gpio_is_valid(4) == false){
printk("GPIO is no valid\n");
goto err_device;
}
/* Requesting the GPIO */
if(gpio_request(4, "gpio-4") < 0){
printk("GPIO not requested\n");
goto err_gpio;
}
/* Configure GPIO as output */
if(gpio_direction_output(4,0)){
printk("Can not set GPIO to output\n");
goto err_gpio;
}
/* For debugging purposes we can export the GPIO which is allocated
* usig the gpio_request - /sys/class/gpio/gpio* */
gpio_export(4, false);
printk("Sao 4.30 da manha... e eu ainda aqui!\n");
return 0;
err_gpio:
gpio_free(4);
err_device:
device_destroy (gpio_class, devnr);
err_class:
class_destroy(gpio_class);
err_alloc:
unregister_chrdev_region(devnr, 1);
return -1;
}
static int gpio_driver_open(struct inode *inode, struct file *filp)
{
if (deviceopen)
{
printk(KERN_WARNING "Already open\n");
return -EBUSY;
}
try_module_get(THIS_MODULE);
deviceopen = 1;
return 0;
}
int gpio_driver_release(struct inode *inode, struct file *filp)
{
deviceopen = 0;
module_put(THIS_MODULE);
return 0;
}
/* Read data out of the buffer */
static ssize_t gpio_driver_read(struct file *filp, char __user *buf, size_t count, loff_t * offset)
{
uint8_t gpio_value = 0;
int i;
int last = *offset + count;
/* Read GPIO value */
gpio_value = gpio_get_value(4);
if (last > size)
{
last = size;
}
i = last - *offset;
if (i == 0)
{
return 0;
}
if (copy_to_user (buf, &gpio_value + *offset, i)) //VERIFICAR
{
return -EFAULT;
}
return i;
}
/* Write data to buffer */
static ssize_t gpio_driver_write(struct file *filp, const char __user *buf, size_t count, loff_t * offset)
{
char gpio_read;
int i, n;
int last = *offset + count;
if (last > MAX_FILE_SIZE)
last = MAX_FILE_SIZE;
i = last - *offset;
n = copy_from_user (&gpio_read + *offset, buf, i);
if (n != 0)
{
pr_warn ("Could not copy %d bytes\n", n);
return -EFAULT;
}
*offset += i;
if (size < *offset) //keep data from previous accesses
size = *offset;
/* Setting the LED - 0 or 1*/
if (n == '0'){
gpio_set_value(4,0);
}
else if (n == '1'){
gpio_set_value(4,1);
}
else {
printk("Invalid input\n");
}
return i;
}
/*
static void my_timer_func(struct timer_list *unused){
led_status = ~led_status;
outb(led_status, 0x378);
my_timer.expires += blink_delay;
add_timer(&my_timer);
}
*/
void gpio_driver_exit(void){
//gpio_unexport(4);
gpio_free(4);
device_destroy(gpio_class, devnr);
class_destroy(gpio_class);
cdev_del(&LED_cdev);
unregister_chrdev_region(devnr, 1);
printk("Live long and prosper\n");
}
Related
I have built a custom hardware configuration in Vivado for Xilinx SoC board, and used petalinux to create a custom driver to control the hardware logic. It seems like after running insmod command, the driver is never initialized and the ->probe() function is not called.
I am new to this domain, and wondering if anyone ran into a similar issue and has some pointers on where and what to check in order to see where the issue is. Any advice would be very helpful!
Running the dmesg command after inserting the driver into the kernel:
root#plzwork3:/proc/device-tree/amba_pl#0/simpleMultiplier#a0000000# dmesg
[ 3351.680317] <1>Hello module world.
[ 3351.683735] <1>Module parameters were (0xdeadbeef) and "default"
Device Tree entity created by Vivado:
/ {
amba_pl: amba_pl#0 {
#address-cells = <2>;
#size-cells = <2>;
compatible = "simple-bus";
ranges ;
simpleMultiplier_0: simpleMultiplier#a0000000 {
clock-names = "axiliteregport_aclk";
clocks = <&zynqmp_clk 71>;
compatible = "xlnx,simpleMultiplier-1.0";
reg = <0x0 0xa0000000 0x0 0x10000>;
xlnx,axiliteregport-addr-width = <0x4>;
xlnx,axiliteregport-data-width = <0x20>;
};
};
};
Device driver code snippets:
#ifdef CONFIG_OF
static struct of_device_id simpmod_of_match[] = {
{ .compatible = "xlnx,simpleMultiplier-1.0", },
{ /* end of list */ },
};
Full device driver code:
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/fs.h>
/* Standard module information, edit as appropriate */
MODULE_LICENSE("GPL");
MODULE_AUTHOR
("Xilinx Inc.");
MODULE_DESCRIPTION("simpmod - loadable module template generated by petalinux-create -t modules");
#define DRIVER_NAME "simpmod"
#define CLASS_NAME "CLASS_TUT"
static char ker_buf[100];
static int currLen = 0;
// Parts of the math operation (2,2,+) is 2+2
static int operand_1 = 0;
static int operand_2 = 0;
static int result = 0;
static struct class *driver_class = NULL;
static dev_t first;
static struct cdev c_dev; // Global variable for the character device
static struct device *ourDevice;
// Pointer to the IP registers
volatile unsigned int *regA;
volatile unsigned int *regB;
volatile unsigned int *regC;
// Structure to hold device specific data
struct simpmod_local {
int irq;
unsigned long mem_start;
unsigned long mem_end;
void __iomem *base_addr;
};
// Character callbacks prototype
static int dev_open(struct inode *inod, struct file *fil);
static ssize_t dev_read(struct file *fil, char *buf, size_t len, loff_t *off);
static ssize_t dev_write(struct file *fil, const char *buf, size_t len, loff_t *off);
static int dev_release(struct inode *inod, struct file *fil);
static struct file_operations fops = {
.read=dev_read,
.write=dev_write,
.open=dev_open,
.release=dev_release,
};
static irqreturn_t simpmod_irq(int irq, void *lp){
printk("simpmod interrupt\n");
return IRQ_HANDLED;
}
static int simpmod_probe(struct platform_device *pdev){
struct resource *r_irq; /* Interrupt resources */
struct resource *r_mem; /* IO mem resources */
struct device *dev = &pdev->dev;
static struct simpmod_local *lp = NULL;
int rc = 0;
printk("Device Tree Probing\n");
// Get data of type IORESOURCE_MEM(reg-addr) from the device-tree
// Other types defined here:
// http://lxr.free-electrons.com/source/include/linux/ioport.h#L33
r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r_mem) {
dev_err(dev, "invalid address\n");
return -ENODEV;
}
// Allocate memory (continuous physical)to hold simpmod_local struct
lp = (struct simpmod_local *) kmalloc(sizeof(struct simpmod_local), GFP_KERNEL);
if (!lp) {
printk("Cound not allocate simpmod device\n");
return -ENOMEM;
}
dev_set_drvdata(dev, lp);
// Save data on simpmod_local strucutre
lp->mem_start = r_mem->start;
lp->mem_end = r_mem->end;
// Ask the kernel the memory region defined on the device-tree and
// prevent other drivers to overlap on this region
// This is needed before the ioremap
if (!request_mem_region(lp->mem_start,
lp->mem_end - lp->mem_start + 1,
DRIVER_NAME)) {
dev_err(dev, "Couldn't lock memory region at %p\n",
(void *)lp->mem_start);
rc = -EBUSY;
goto error1;
}
// Get an virtual address from the device physical address with a
// range size: lp->mem_end - lp->mem_start + 1
lp->base_addr = ioremap(lp->mem_start, lp->mem_end - lp->mem_start + 1);
if (!lp->base_addr) {
dev_err(dev, "simpmod: Could not allocate iomem\n");
rc = -EIO;
goto error2;
}
// ****************** NORMAL Device diver *************************
// register a range of char device numbers
if (alloc_chrdev_region(&first, 0, 1, "Leonardo") < 0){
printk(KERN_ALERT "alloc_chrdev_region failed\n");
return -1;
}
// Create class (/sysfs)
driver_class = class_create(THIS_MODULE, CLASS_NAME);
if (driver_class == NULL) {
printk(KERN_ALERT "Create class failed\n");
unregister_chrdev_region(first, 1);
return -1;
}
ourDevice = device_create(driver_class, NULL, first, NULL, "tutDevice");
if ( ourDevice == NULL){
printk(KERN_ALERT "Create device failed\n");
class_destroy(driver_class);
unregister_chrdev_region(first, 1);
return -1;
}
// Create a character device /dev/tutDevice
cdev_init(&c_dev, &fops);
if (cdev_add(&c_dev, first, 1) == -1){
printk(KERN_ALERT "Create character device failed\n");
device_destroy(driver_class, first);
class_destroy(driver_class);
unregister_chrdev_region(first, 1);
return -1;petalinux-config -c rootfs
}
// Create the attribute file on /sysfs/class/CLASS_TUT/ called
// parCrtl and isBusy
// Get data of type IORESOURCE_IRQ(interrupt) from the device-tree
r_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!r_irq) {
printk("no IRQ found\n");
printk("simpmod at 0x%08x mapped to 0x%08x\n",
(unsigned int __force)lp->mem_start,
(unsigned int __force)lp->base_addr);
// Configuring pointers to the IP registers
regA = (unsigned int __force)lp->base_addr + 0x10;
regB = (unsigned int __force)lp->base_addr + 0x18;
regC = (unsigned int __force)lp->base_addr + 0x26;
printk("regA: 0x%08x\n",(unsigned int)regA);
printk("regB: 0x%08x\n",(unsigned int)regB);
printk("regC: 0x%08x\n",(unsigned int)regC);
return 0;
}
lp->irq = r_irq->start;
rc = request_irq(lp->irq, &simpmod_irq, 0, DRIVER_NAME, lp);
if (rc) {
dev_err(dev, "testmodule: Could not allocate interrupt %d.\n",
lp->irq);
goto error3;
}
return 0;
error3:
free_irq(lp->irq, lp);
error2:
release_mem_region(lp->mem_start, lp->mem_end - lp->mem_start + 1);
error1:
kfree(lp);
dev_set_drvdata(dev, NULL);
return rc;
}
static int simpmod_remove(struct platform_device *pdev){
struct device *dev = &pdev->dev;
struct simpmod_local *lp = dev_get_drvdata(dev);
free_irq(lp->irq, lp);
release_mem_region(lp->mem_start, lp->mem_end - lp->mem_start + 1);
kfree(lp);
dev_set_drvdata(dev, NULL);
return 0;
}
// Indicate which type of hardware we handle on this case(simpleAlu-1.0)
#ifdef CONFIG_OF
static struct of_device_id simpmod_of_match[] = {
{ .compatible = "xlnx,simpleMultiplier-1.0", },
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(of, simpmod_of_match);
#else
# define simpmod_of_match
#endif
static struct platform_driver simpmod_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = simpmod_of_match,
},
.probe = simpmod_probe,
.remove = simpmod_remove,
};
static int __init simpmod_init(void)
{
printk("<1>Simple device driver.\n");
printk("Hussam was here, and he init the module\n");
return platform_driver_register(&simpmod_driver);
}
static void __exit simpmod_exit(void)
{
platform_driver_unregister(&simpmod_driver);
printk(KERN_ALERT "Goodbye module world.\n");
}
static int dev_open(struct inode *inod, struct file *fil){
printk(KERN_ALERT "Character device opened\n");
return 0;
}
// Just send to the user a string with the value of result
static ssize_t dev_read(struct file *fil, char *buf, size_t len, loff_t *off){
// Return the result only once (otherwise a simple cat will loop)
// Copy from kernel space to user space
printk(KERN_ALERT "Reading device rx: %d\n",(int)len);
int n = sprintf(ker_buf, "%d\n", *regC);
// Copy back to user the result (to,from,size)
copy_to_user(buf,ker_buf,n);
printk(KERN_ALERT "Returning %s rx: %d\n",ker_buf,n);
return n;
}
// Parse the input stream ex: "50,2,*" to some operand variables.
static ssize_t dev_write(struct file *fil, const char *buf, size_t len, loff_t *off){
// Get data from user space to kernel space
copy_from_user(ker_buf,buf,len);
sscanf (ker_buf,"%d,%d,%c",&operand_1,&operand_2);
ker_buf[len] = 0;
// Change the IP registers to the parsed operands (on rega and regb)
*regA = (unsigned int)operand_1;
*regB = (unsigned int)operand_2;
printk(KERN_ALERT "Receiving math operation <%d %d>\n",operand_1,operand_2);
return len;
}
static int dev_release(struct inode *inod, struct file *fil){
printk(KERN_ALERT "Device closed\n");
return 0;
}
module_init(simpmod_init);
module_exit(simpmod_exit);
the problem is, that the (compatible) names in your device tree does not match with those in your code:
Device Tree
compatible = "simple-bus";
Code:
#ifdef CONFIG_OF
static struct of_device_id simpmod_of_match[] = {
{ .compatible = "xlnx,simpleMultiplier-1.0", },
{ /* end of list */ },
};
Put your device tree:
compatible = "xlnx,simpleMultiplier-1.0";
Both entries should match!
Now your "probe" function should be called!
BR
Andreas
your of_match table section in the driver code:
// Indicate which type of hardware we handle on this case(simpleAlu-1.0)
#ifdef CONFIG_OF
static struct of_device_id simpmod_of_match[] = {
{ .compatible = "xlnx,simpleMultiplier-1.0", },
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(of, simpmod_of_match);
#else
# define simpmod_of_match
#endif
should be like this:
static struct of_device_id simpmod_of_match[] = {
{ .compatible = "xlnx,simpleMultiplier-1.0", },
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(of, simpmod_of_match);
and make sure the compatible line strings have to match in the pl.dtsi and of_match table
I'm trying to access an 24c256 eeprom content from user space in a am335x_starter_kit.
I dont have to add eeprom driver into kernel and make modifications in board.c file because board already uses eeprom to access some board configuration and Mac address information.
I just want to access eeprom content from user space.
I used read and write functions for character devices before but i2c platform devices doesnt have these functions.
struct i2c_driver {
unsigned int class;
int (* attach_adapter) (struct i2c_adapter *);
int (* probe) (struct i2c_client *, const struct i2c_device_id *);
int (* remove) (struct i2c_client *);
void (* shutdown) (struct i2c_client *);
void (* alert) (struct i2c_client *, unsigned int data);
int (* command) (struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;
const struct i2c_device_id * id_table;
int (* detect) (struct i2c_client *, struct i2c_board_info *);
const unsigned short * address_list;
struct list_head clients;
};
This is the eeprom driver. Board file uses it from kernel to get mac address and board configuration data.
/*
* at24.c - handle most I2C EEPROMs
*
* Copyright (C) 2005-2007 David Brownell
* Copyright (C) 2008 Wolfram Sang, Pengutronix
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/sysfs.h>
#include <linux/mod_devicetable.h>
#include <linux/log2.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/of.h>
#include <linux/i2c.h>
#include <linux/i2c/at24.h>
/*
* I2C EEPROMs from most vendors are inexpensive and mostly interchangeable.
* Differences between different vendor product lines (like Atmel AT24C or
* MicroChip 24LC, etc) won't much matter for typical read/write access.
* There are also I2C RAM chips, likewise interchangeable. One example
* would be the PCF8570, which acts like a 24c02 EEPROM (256 bytes).
*
* However, misconfiguration can lose data. "Set 16-bit memory address"
* to a part with 8-bit addressing will overwrite data. Writing with too
* big a page size also loses data. And it's not safe to assume that the
* conventional addresses 0x50..0x57 only hold eeproms; a PCF8563 RTC
* uses 0x51, for just one example.
*
* Accordingly, explicit board-specific configuration data should be used
* in almost all cases. (One partial exception is an SMBus used to access
* "SPD" data for DRAM sticks. Those only use 24c02 EEPROMs.)
*
* So this driver uses "new style" I2C driver binding, expecting to be
* told what devices exist. That may be in arch/X/mach-Y/board-Z.c or
* similar kernel-resident tables; or, configuration data coming from
* a bootloader.
*
* Other than binding model, current differences from "eeprom" driver are
* that this one handles write access and isn't restricted to 24c02 devices.
* It also handles larger devices (32 kbit and up) with two-byte addresses,
* which won't work on pure SMBus systems.
*/
struct at24_data {
struct at24_platform_data chip;
struct memory_accessor macc;
int use_smbus;
/*
* Lock protects against activities from other Linux tasks,
* but not from changes by other I2C masters.
*/
struct mutex lock;
struct bin_attribute bin;
u8 *writebuf;
unsigned write_max;
unsigned num_addresses;
/*
* Some chips tie up multiple I2C addresses; dummy devices reserve
* them for us, and we'll use them with SMBus calls.
*/
struct i2c_client *client[];
};
/*
* This parameter is to help this driver avoid blocking other drivers out
* of I2C for potentially troublesome amounts of time. With a 100 kHz I2C
* clock, one 256 byte read takes about 1/43 second which is excessive;
* but the 1/170 second it takes at 400 kHz may be quite reasonable; and
* at 1 MHz (Fm+) a 1/430 second delay could easily be invisible.
*
* This value is forced to be a power of two so that writes align on pages.
*/
static unsigned io_limit = 128;
module_param(io_limit, uint, 0);
MODULE_PARM_DESC(io_limit, "Maximum bytes per I/O (default 128)");
/*
* Specs often allow 5 msec for a page write, sometimes 20 msec;
* it's important to recover from write timeouts.
*/
static unsigned write_timeout = 25;
module_param(write_timeout, uint, 0);
MODULE_PARM_DESC(write_timeout, "Time (in ms) to try writes (default 25)");
#define AT24_SIZE_BYTELEN 5
#define AT24_SIZE_FLAGS 8
#define AT24_BITMASK(x) (BIT(x) - 1)
/* create non-zero magic value for given eeprom parameters */
#define AT24_DEVICE_MAGIC(_len, _flags) \
((1 << AT24_SIZE_FLAGS | (_flags)) \
<< AT24_SIZE_BYTELEN | ilog2(_len))
static const struct i2c_device_id at24_ids[] = {
/* needs 8 addresses as A0-A2 are ignored */
{ "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },
/* old variants can't be handled with this generic entry! */
{ "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },
{ "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },
/* spd is a 24c02 in memory DIMMs */
{ "spd", AT24_DEVICE_MAGIC(2048 / 8,
AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
{ "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },
/* 24rf08 quirk is handled at i2c-core */
{ "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },
{ "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },
{ "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },
{ "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },
{ "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },
{ "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },
{ "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },
{ "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },
{ "at24", 0 },
{ /* END OF LIST */ }
};
MODULE_DEVICE_TABLE(i2c, at24_ids);
/*-------------------------------------------------------------------------*/
/*
* This routine supports chips which consume multiple I2C addresses. It
* computes the addressing information to be used for a given r/w request.
* Assumes that sanity checks for offset happened at sysfs-layer.
*/
static struct i2c_client *at24_translate_offset(struct at24_data *at24,
unsigned *offset)
{
unsigned i;
if (at24->chip.flags & AT24_FLAG_ADDR16) {
i = *offset >> 16;
*offset &= 0xffff;
} else {
i = *offset >> 8;
*offset &= 0xff;
}
return at24->client[i];
}
static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
unsigned offset, size_t count)
{
struct i2c_msg msg[2];
u8 msgbuf[2];
struct i2c_client *client;
unsigned long timeout, read_time;
int status, i;
memset(msg, 0, sizeof(msg));
/*
* REVISIT some multi-address chips don't rollover page reads to
* the next slave address, so we may need to truncate the count.
* Those chips might need another quirk flag.
*
* If the real hardware used four adjacent 24c02 chips and that
* were misconfigured as one 24c08, that would be a similar effect:
* one "eeprom" file not four, but larger reads would fail when
* they crossed certain pages.
*/
/*
* Slave address and byte offset derive from the offset. Always
* set the byte address; on a multi-master board, another master
* may have changed the chip's "current" address pointer.
*/
client = at24_translate_offset(at24, &offset);
if (count > io_limit)
count = io_limit;
switch (at24->use_smbus) {
case I2C_SMBUS_I2C_BLOCK_DATA:
/* Smaller eeproms can work given some SMBus extension calls */
if (count > I2C_SMBUS_BLOCK_MAX)
count = I2C_SMBUS_BLOCK_MAX;
break;
case I2C_SMBUS_WORD_DATA:
count = 2;
break;
case I2C_SMBUS_BYTE_DATA:
count = 1;
break;
default:
/*
* When we have a better choice than SMBus calls, use a
* combined I2C message. Write address; then read up to
* io_limit data bytes. Note that read page rollover helps us
* here (unlike writes). msgbuf is u8 and will cast to our
* needs.
*/
i = 0;
if (at24->chip.flags & AT24_FLAG_ADDR16)
msgbuf[i++] = offset >> 8;
msgbuf[i++] = offset;
msg[0].addr = client->addr;
msg[0].buf = msgbuf;
msg[0].len = i;
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].buf = buf;
msg[1].len = count;
}
/*
* Reads fail if the previous write didn't complete yet. We may
* loop a few times until this one succeeds, waiting at least
* long enough for one entire page write to work.
*/
timeout = jiffies + msecs_to_jiffies(write_timeout);
do {
read_time = jiffies;
switch (at24->use_smbus) {
case I2C_SMBUS_I2C_BLOCK_DATA:
status = i2c_smbus_read_i2c_block_data(client, offset,
count, buf);
break;
case I2C_SMBUS_WORD_DATA:
status = i2c_smbus_read_word_data(client, offset);
if (status >= 0) {
buf[0] = status & 0xff;
buf[1] = status >> 8;
status = count;
}
break;
case I2C_SMBUS_BYTE_DATA:
status = i2c_smbus_read_byte_data(client, offset);
if (status >= 0) {
buf[0] = status;
status = count;
}
break;
default:
status = i2c_transfer(client->adapter, msg, 2);
if (status == 2)
status = count;
}
dev_dbg(&client->dev, "read %zu#%d --> %d (%ld)\n",
count, offset, status, jiffies);
if (status == count)
return count;
/* REVISIT: at HZ=100, this is sloooow */
msleep(1);
} while (time_before(read_time, timeout));
return -ETIMEDOUT;
}
static ssize_t at24_read(struct at24_data *at24,
char *buf, loff_t off, size_t count)
{
ssize_t retval = 0;
if (unlikely(!count))
return count;
/*
* Read data from chip, protecting against concurrent updates
* from this host, but not from other I2C masters.
*/
mutex_lock(&at24->lock);
while (count) {
ssize_t status;
status = at24_eeprom_read(at24, buf, off, count);
if (status <= 0) {
if (retval == 0)
retval = status;
break;
}
buf += status;
off += status;
count -= status;
retval += status;
}
mutex_unlock(&at24->lock);
return retval;
}
static ssize_t at24_bin_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct at24_data *at24;
at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
return at24_read(at24, buf, off, count);
}
/*
* Note that if the hardware write-protect pin is pulled high, the whole
* chip is normally write protected. But there are plenty of product
* variants here, including OTP fuses and partial chip protect.
*
* We only use page mode writes; the alternative is sloooow. This routine
* writes at most one page.
*/
static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
unsigned offset, size_t count)
{
struct i2c_client *client;
struct i2c_msg msg;
ssize_t status;
unsigned long timeout, write_time;
unsigned next_page;
/* Get corresponding I2C address and adjust offset */
client = at24_translate_offset(at24, &offset);
/* write_max is at most a page */
if (count > at24->write_max)
count = at24->write_max;
/* Never roll over backwards, to the start of this page */
next_page = roundup(offset + 1, at24->chip.page_size);
if (offset + count > next_page)
count = next_page - offset;
/* If we'll use I2C calls for I/O, set up the message */
if (!at24->use_smbus) {
int i = 0;
msg.addr = client->addr;
msg.flags = 0;
/* msg.buf is u8 and casts will mask the values */
msg.buf = at24->writebuf;
if (at24->chip.flags & AT24_FLAG_ADDR16)
msg.buf[i++] = offset >> 8;
msg.buf[i++] = offset;
memcpy(&msg.buf[i], buf, count);
msg.len = i + count;
}
/*
* Writes fail if the previous one didn't complete yet. We may
* loop a few times until this one succeeds, waiting at least
* long enough for one entire page write to work.
*/
timeout = jiffies + msecs_to_jiffies(write_timeout);
do {
write_time = jiffies;
if (at24->use_smbus) {
status = i2c_smbus_write_i2c_block_data(client,
offset, count, buf);
if (status == 0)
status = count;
} else {
status = i2c_transfer(client->adapter, &msg, 1);
if (status == 1)
status = count;
}
dev_dbg(&client->dev, "write %zu#%d --> %zd (%ld)\n",
count, offset, status, jiffies);
if (status == count)
return count;
/* REVISIT: at HZ=100, this is sloooow */
msleep(1);
} while (time_before(write_time, timeout));
return -ETIMEDOUT;
}
static ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off,
size_t count)
{
ssize_t retval = 0;
if (unlikely(!count))
return count;
/*
* Write data to chip, protecting against concurrent updates
* from this host, but not from other I2C masters.
*/
mutex_lock(&at24->lock);
while (count) {
ssize_t status;
status = at24_eeprom_write(at24, buf, off, count);
if (status <= 0) {
if (retval == 0)
retval = status;
break;
}
buf += status;
off += status;
count -= status;
retval += status;
}
mutex_unlock(&at24->lock);
return retval;
}
static ssize_t at24_bin_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct at24_data *at24;
at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
return at24_write(at24, buf, off, count);
}
/*-------------------------------------------------------------------------*/
/*
* This lets other kernel code access the eeprom data. For example, it
* might hold a board's Ethernet address, or board-specific calibration
* data generated on the manufacturing floor.
*/
static ssize_t at24_macc_read(struct memory_accessor *macc, char *buf,
off_t offset, size_t count)
{
struct at24_data *at24 = container_of(macc, struct at24_data, macc);
return at24_read(at24, buf, offset, count);
}
static ssize_t at24_macc_write(struct memory_accessor *macc, const char *buf,
off_t offset, size_t count)
{
struct at24_data *at24 = container_of(macc, struct at24_data, macc);
return at24_write(at24, buf, offset, count);
}
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_OF
static void at24_get_ofdata(struct i2c_client *client,
struct at24_platform_data *chip)
{
const __be32 *val;
struct device_node *node = client->dev.of_node;
if (node) {
if (of_get_property(node, "read-only", NULL))
chip->flags |= AT24_FLAG_READONLY;
val = of_get_property(node, "pagesize", NULL);
if (val)
chip->page_size = be32_to_cpup(val);
}
}
#else
static void at24_get_ofdata(struct i2c_client *client,
struct at24_platform_data *chip)
{ }
#endif /* CONFIG_OF */
static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct at24_platform_data chip;
bool writable;
int use_smbus = 0;
struct at24_data *at24;
int err;
unsigned i, num_addresses;
kernel_ulong_t magic;
if (client->dev.platform_data) {
chip = *(struct at24_platform_data *)client->dev.platform_data;
} else {
if (!id->driver_data) {
err = -ENODEV;
goto err_out;
}
magic = id->driver_data;
chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
magic >>= AT24_SIZE_BYTELEN;
chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);
/*
* This is slow, but we can't know all eeproms, so we better
* play safe. Specifying custom eeprom-types via platform_data
* is recommended anyhow.
*/
chip.page_size = 1;
/* update chipdata if OF is present */
at24_get_ofdata(client, &chip);
chip.setup = NULL;
chip.context = NULL;
}
if (!is_power_of_2(chip.byte_len))
dev_warn(&client->dev,
"byte_len looks suspicious (no power of 2)!\n");
if (!chip.page_size) {
dev_err(&client->dev, "page_size must not be 0!\n");
err = -EINVAL;
goto err_out;
}
if (!is_power_of_2(chip.page_size))
dev_warn(&client->dev,
"page_size looks suspicious (no power of 2)!\n");
/* Use I2C operations unless we're stuck with SMBus extensions. */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
if (chip.flags & AT24_FLAG_ADDR16) {
err = -EPFNOSUPPORT;
goto err_out;
}
if (i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;
} else if (i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_WORD_DATA)) {
use_smbus = I2C_SMBUS_WORD_DATA;
} else if (i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
use_smbus = I2C_SMBUS_BYTE_DATA;
} else {
err = -EPFNOSUPPORT;
goto err_out;
}
}
//???????????????
if (chip.flags & AT24_FLAG_TAKE8ADDR)
num_addresses = 8;
else
num_addresses = DIV_ROUND_UP(chip.byte_len, (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
at24 = kzalloc(sizeof(struct at24_data) + num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);
if (!at24) {
err = -ENOMEM;
goto err_out;
}
mutex_init(&at24->lock);
at24->use_smbus = use_smbus;
at24->chip = chip;
at24->num_addresses = num_addresses;
/*
* Export the EEPROM bytes through sysfs, since that's convenient.
* By default, only root should see the data (maybe passwords etc)
*/
sysfs_bin_attr_init(&at24->bin);
at24->bin.attr.name = "eeprom";
at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;
at24->bin.read = at24_bin_read;
at24->bin.size = chip.byte_len;
at24->macc.read = at24_macc_read;
writable = !(chip.flags & AT24_FLAG_READONLY);
if (writable) {
if (!use_smbus || i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
unsigned write_max = chip.page_size;
at24->macc.write = at24_macc_write;
at24->bin.write = at24_bin_write;
at24->bin.attr.mode |= S_IWUSR;
if (write_max > io_limit)
write_max = io_limit;
if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)
write_max = I2C_SMBUS_BLOCK_MAX;
at24->write_max = write_max;
/* buffer (data + address at the beginning) */
at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL);
if (!at24->writebuf) {
err = -ENOMEM;
goto err_struct;
}
} else {
dev_warn(&client->dev,
"cannot write due to controller restrictions.");
}
}
at24->client[0] = client;
/* use dummy devices for multiple-address chips */
for (i = 1; i < num_addresses; i++) {
at24->client[i] = i2c_new_dummy(client->adapter,
client->addr + i);
if (!at24->client[i]) {
dev_err(&client->dev, "address 0x%02x unavailable\n",
client->addr + i);
err = -EADDRINUSE;
goto err_clients;
}
}
err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);
if (err)
goto err_clients;
i2c_set_clientdata(client, at24);
dev_info(&client->dev, "%zu byte %s EEPROM, %s, %u bytes/write\n", at24->bin.size, client->name,
writable ? "writable" : "read-only", at24->write_max);
if (use_smbus == I2C_SMBUS_WORD_DATA ||
use_smbus == I2C_SMBUS_BYTE_DATA) {
dev_notice(&client->dev, "Falling back to %s reads, "
"performance will suffer\n", use_smbus ==
I2C_SMBUS_WORD_DATA ? "word" : "byte");
}
/* export data to kernel code */
if (chip.setup)
chip.setup(&at24->macc, chip.context);
return 0;
err_clients:
for (i = 1; i < num_addresses; i++)
if (at24->client[i])
i2c_unregister_device(at24->client[i]);
kfree(at24->writebuf);
err_struct:
kfree(at24);
err_out:
dev_dbg(&client->dev, "probe error %d\n", err);
return err;
}
/*-------------------------------------------------------------------------*/
static int __devexit at24_remove(struct i2c_client *client)
{
struct at24_data *at24;
int i;
at24 = i2c_get_clientdata(client);
sysfs_remove_bin_file(&client->dev.kobj, &at24->bin);
for (i = 1; i < at24->num_addresses; i++)
i2c_unregister_device(at24->client[i]);
kfree(at24->writebuf);
kfree(at24);
return 0;
}
/*-------------------------------------------------------------------------*/
static struct i2c_driver at24_driver = {
.driver = {
.name = "at24",
.owner = THIS_MODULE,
},
.probe = at24_probe,
.remove = __devexit_p(at24_remove),
.id_table = at24_ids,
};
static int __init at24_init(void)
{
if (!io_limit) {
pr_err("at24: io_limit must not be 0!\n");
return -EINVAL;
}
io_limit = rounddown_pow_of_two(io_limit);
return i2c_add_driver(&at24_driver);
}
module_init(at24_init);
static void __exit at24_exit(void)
{
i2c_del_driver(&at24_driver);
}
module_exit(at24_exit);
MODULE_DESCRIPTION("Driver for most I2C EEPROMs");
MODULE_AUTHOR("David Brownell and Wolfram Sang");
MODULE_LICENSE("GPL");
These are snippets from board file:
static struct i2c_board_info __initdata am335x_i2c0_boardinfo[] = {
{
/* Baseboard board EEPROM */
I2C_BOARD_INFO("24c256", BASEBOARD_I2C_ADDR),
.platform_data = &am335x_baseboard_eeprom_info,
},
.
.
static struct at24_platform_data am335x_baseboard_eeprom_info = {
.byte_len = (256*1024) / 8,
.page_size = 64,
.flags = AT24_FLAG_ADDR16,
.setup = am335x_evm_setup,
.context = (void *)NULL,
};
static void am335x_evm_setup(struct memory_accessor *mem_acc, void *context)
{
int ret;
char tmp[10];
struct device *mpu_dev;
/* 1st get the MAC address from EEPROM */
ret = mem_acc->read(mem_acc, (char *)&am335x_mac_addr,
EEPROM_MAC_ADDRESS_OFFSET, sizeof(am335x_mac_addr));
.
.
.
How can i read from/write into eeprom content from user space.
Should i use sysfs? What should i do?
EEPROM:
It's part of setting the MAC and serial number, but the only way to know if the EEPROM is working is to read its content.
$ cat /sys/bus/i2c/devices/2-0057/eeprom | hexdump -C
I am working on parallel port driver. Now i had seen the methods to get interrupt from parallel port.
by one of them,
First make 4th pin of control reg 1(IRQ).
then make nACK low.
so i make a switch in between data pin 8 and nACK. so, if i write some data which has msb 1 then it will be interrupted, if that switch is on. Now i have a problem. If i disconnected that switch and again connect then it will not give me interrupt.
So, how can i do that thing by which i got interrupt by means of switch is connect or not.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/parport.h>
#include <asm/uaccess.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <linux/errno.h>
#include <asm/irq.h>
#include <linux/kthread.h>
#define DEVICE_NAME "parlelport"
struct pardevice *pdev;
static int dummy;
int ret;
static irqreturn_t recv_handler(int irq, void *dev_id)
{
printk("we inside if isr");
return 0;
}
int led_open(struct inode *inode, struct file *file)
{
printk("1\n");
printk("Device File Opened\n");
char byte1;
byte1=inb(0x37A);
printk("%d \n",byte1);
return 0;
}
ssize_t led_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
printk("2\n");
char byte=inb(0x37A);
printk("%d",byte);
byte = byte | 0x10; // 0x10= 00010000, 4th pin of CTRL reg
outb(byte, 0x37A); //which enable IRQ
char kbuf;
copy_from_user(&kbuf, buf, 1);
parport_claim_or_block(pdev); /* Claim the port */
parport_write_data(pdev->port, kbuf); /* Write to the device */
//parport_release (pdev);
return count;
}
int led_release(struct inode *inode, struct file *file)
{
printk("3\n");
printk("Device File Released\n");
char byte;
byte=inb(0x37A);
printk("%d", byte);
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
.release = led_release,
};
static int led_preempt(void *handle)
{
printk("4\n");
return 1;
}
static void led_attach(struct parport *port)
{
printk("5\n");
pdev = parport_register_device(port, DEVICE_NAME, led_preempt, NULL, NULL, 0, NULL);
printk("Port attached\n");
char byte1;
byte1=inb(0x37A);
printk("%d \n",byte1);
}
static void led_detach(struct parport *port)
{
printk("6\n");
parport_unregister_device (pdev);
printk("Port Deattached\n");
}
static struct parport_driver led_driver = {
.name= "led",
.attach = led_attach,
.detach = led_detach,
};
int __init led_init(void)
{
printk("7\n");
if (register_chrdev(89, DEVICE_NAME, &led_fops))
{
printk("Can't register device\n");
return -1;
}
char byte=inb(0x37A);
printk("%d",byte);
byte = byte | 0x10;
outb(byte, 0x37A);
char byte1;
byte1=inb(0x37A);
printk("%d %d \n",byte,byte1);
parport_register_driver(&led_driver);
ret= request_irq(7, recv_handler, IRQF_SHARED, "parlelport", &dummy);
printk("%d",ret);
return 0;
}
void __exit led_cleanup(void)
{
printk("8\n");
unregister_chrdev(89, DEVICE_NAME);
if(!ret)
free_irq(7, &dummy);
parport_unregister_driver(&led_driver);
printk("LED Driver unregistered.\n");
return;
}
module_init(led_init);
module_exit(led_cleanup);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Vikrant Patel");
Test.c file
int main()
{
int fd=open("/dev/parlelport",O_RDWR);
char byte;
printf("Enter Value to send on parallel port");
scanf("%c",&byte);
printf("Byte value is %c\n",byte);
if(write(fd,&byte,sizeof(char)))
{
printf("\nSuccessfully written on port");
}
getchar();
getchar();
close(fd);
}
I got it.
First make a thread
put the Enable IRQ code in that thread
so it will continuously execute it
whenever i connect pins at my hardware then it will be interrupted.
check this code for your ref.
#include <linux/module.h>
#include <linux/parport.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <linux/kthread.h>
#define DEVICE_NAME "parlelport"
#define DATA 0x378
#define STATUS 0x379
#define CONTROL 0x37A
struct pardevice *pdev;
struct task_struct *ts1, *ts2;
int dummy;
char buf1='1',buf2='2';
char byte='0';
int thread1(void *data)
{
while(1)
{
outb(byte, CONTROL); /* 0x30 = 0011 0000 , makes IRQ pin(5th bit) enable */
printk("Thread1\n");
parport_claim_or_block(pdev); /* Claim the port */
parport_write_data(pdev->port, buf1); /* Write to the device */
parport_release(pdev); /* Release the port */
msleep(4000);
if (kthread_should_stop())
break;
}
return 0;
}
int thread2(void *data)
{
while(1)
{
outb(byte,CONTROL); /* 0x30 = 0011 0000 , makes IRQ pin(5th bit) enable */
printk("Thread2\n");
parport_claim_or_block(pdev); /* Claim the port */
parport_write_data(pdev->port, buf2); /* Write to the device */
parport_release(pdev); /* Release the port */
msleep(4000);
if (kthread_should_stop())
break;
}
return 0;
}
int led_open(struct inode *inode, struct file *file)
{
printk("Device File Opened\n");
ts1=kthread_run(thread1,NULL,"kthread"); /* Initiation of thread 1 */
msleep(2000);
ts2=kthread_run(thread2,NULL,"kthread"); /* Initiation of thread 2 */
return 0;
}
ssize_t led_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
return count;
}
int led_release(struct inode *inode, struct file *file)
{
printk("Device File Released\n");
kthread_stop(ts1);
kthread_stop(ts2);
buf1='1';
buf2='2';
outb_p(0x00,DATA);
return 0;
}
static irqreturn_t recv_handler(int irq, void *unused)
{
printk("we inside of isr");
buf1= buf1 ^ 0x7F;
buf2= buf2 ^ 0x7F;
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
.release = led_release,
};
static int led_preempt(void *handle)
{
return 1;
}
static void led_attach(struct parport *port)
{
pdev = parport_register_device(port, DEVICE_NAME, led_preempt, NULL, NULL, 0, NULL);
printk("Port attached\n");
}
static void led_detach(struct parport *port)
{
parport_unregister_device (pdev);
printk("Port Deattached\n");
}
static struct parport_driver led_driver = {
.name= "led",
.attach = led_attach,
.detach = led_detach,
};
int __init led_init(void)
{
/*Register our ISR with the kernel for PARALLEL_IRQ */
if (request_irq(7, recv_handler, IRQF_SHARED, DEVICE_NAME ,&dummy))
{
printk("Registering ISR failed\n");
return -ENODEV;
}
/*Register Character Device Driver at 89 Major number*/
if (register_chrdev(89, DEVICE_NAME, &led_fops))
{
printk("Can't register device\n");
return -1;
}
/*Register parallel port driver with parport structure led_driver*/
parport_register_driver(&led_driver);
return 0;
}
void __exit led_cleanup(void)
{
unregister_chrdev(89, DEVICE_NAME); /* Unregister char driver */
free_irq(7, &dummy); /* Free the ISR from IRQ7 */
parport_unregister_driver(&led_driver); /* Unregister the parallel port driver */
printk("LED Driver unregistered.\n");
return;
}
module_init(led_init);
module_exit(led_cleanup);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Vikrant Patel");
I am trying lessons on USB http://www.linuxforu.com/2011/12/data-transfers-to-from-usb-devices/ and stuck with the problem - while reading, usb_bulk_msg returns error 22 - Invalid argument. Write operation succeeds.
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>
#define MIN(a,b) (((a) <= (b)) ? (a) : (b))
#define BULK_EP_OUT 0x01
#define BULK_EP_IN 0x82
#define MAX_PKT_SIZE 512
static struct usb_device *device;
static struct usb_class_driver class;
static unsigned char bulk_buf[MAX_PKT_SIZE];
static int pen_open(struct inode *i, struct file *f)
{
return 0;
}
static int pen_close(struct inode *i, struct file *f)
{
return 0;
}
static ssize_t pen_read(struct file *f, char __user *buf, size_t cnt, loff_t *off)
{
int retval;
int read_cnt;
/* Read the data from the bulk endpoint */
retval = usb_bulk_msg(device, usb_rcvbulkpipe(device, BULK_EP_IN),
bulk_buf, MAX_PKT_SIZE, &read_cnt, 5000);
if (retval)
{
printk(KERN_ERR "Bulk message returned %d\n", retval);
return retval;
}
if (copy_to_user(buf, bulk_buf, MIN(cnt, read_cnt)))
{
return -EFAULT;
}
return MIN(cnt, read_cnt);
}
static ssize_t pen_write(struct file *f, const char __user *buf, size_t cnt, loff_t *off)
{
int retval;
int wrote_cnt = MIN(cnt, MAX_PKT_SIZE);
if (copy_from_user(bulk_buf, buf, MIN(cnt, MAX_PKT_SIZE)))
{
return -EFAULT;
}
/* Write the data into the bulk endpoint */
retval = usb_bulk_msg(device, usb_sndbulkpipe(device, BULK_EP_OUT),
bulk_buf, MIN(cnt, MAX_PKT_SIZE), &wrote_cnt, 5000);
if (retval)
{
printk(KERN_ERR "Bulk message returned %d\n", retval);
return retval;
}
return wrote_cnt;
}
static struct file_operations fops =
{
.open = pen_open,
.release = pen_close,
.read = pen_read,
.write = pen_write,
};
static int pen_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
int retval;
device = interface_to_usbdev(interface);
class.name = "usb/pen%d";
class.fops = &fops;
if ((retval = usb_register_dev(interface, &class)) < 0)
{
/* Something prevented us from registering this driver */
err("Not able to get a minor for this device.");
}
else
{
printk(KERN_INFO "Minor obtained: %d\n", interface->minor);
}
return retval;
}
static void pen_disconnect(struct usb_interface *interface)
{
usb_deregister_dev(interface, &class);
}
/* Table of devices that work with this driver */
static struct usb_device_id pen_table[] =
{
{ USB_DEVICE(0x058F, 0x6387) },
{} /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, pen_table);
static struct usb_driver pen_driver =
{
.name = "pen_driver",
.probe = pen_probe,
.disconnect = pen_disconnect,
.id_table = pen_table,
};
static int __init pen_init(void)
{
int result;
/* Register this driver with the USB subsystem */
if ((result = usb_register(&pen_driver)))
{
err("usb_register failed. Error number %d", result);
}
return result;
}
static void __exit pen_exit(void)
{
/* Deregister this driver with the USB subsystem */
usb_deregister(&pen_driver);
}
module_init(pen_init);
module_exit(pen_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anil Kumar Pugalia <email_at_sarika-pugs_dot_com>");
MODULE_DESCRIPTION("USB Pen Device Driver");
There could be many possible reasons for this error but in my case it happened that the BULK_EP address was wrong. I recommend setting up your endpoint addresses in the probe function rather than hard-coding them. Feel free to refer the below code to setup bulk endpoint addresses.
static void
set_bulk_address (
struct my_device *dev,
struct usb_interface *interface)
{
struct usb_endpoint_descriptor *endpoint;
struct usb_host_interface *iface_desc;
int i;
iface_desc = interface->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
/* check for bulk endpoint */
if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_BULK){
/* bulk in */
if(endpoint->bEndpointAddress & USB_DIR_IN) {
dev->bulk_in_add = endpoint->bEndpointAddress;
dev->bulk_in_size = endpoint->wMaxPacketSize;
dev->bulk_in_buffer = kmalloc(dev->bulk_in_size,
GFP_KERNEL);
if (!dev->bulk_in_buffer)
print("Could not allocate bulk buffer");
}
/* bulk out */
else
dev->bulk_out_add = endpoint->bEndpointAddress;
}
}
}
As you may notice, I have defined my own device struct to hold the endpoint information. Here is my struct definition
struct my_device {
struct usb_device *udev; /* usb device for this device */
struct usb_interface *interface; /* interface for this device */
unsigned char minor; /* minor value */
unsigned char * bulk_in_buffer; /* the buffer to in data */
size_t bulk_in_size; /* the size of the in buffer */
__u8 bulk_in_add; /* bulk in endpoint address */
__u8 bulk_out_add; /* bulk out endpoint address */
struct kref kref; /* module references counter */
};
The error is coming because you have to mention the correct bulk endpoints as per your usb device not the ones given in the example code.
For that you can either check /sys/kernel/debug/usb/devices file or /proc/bus/usb/devices.
In the file check the section containing your device's vendorId and productId and in that section check the E segment for endpoints.
In that segment the one with (I) will be BULK_EP_IN value and the one with (O) will be the value for BULK_EP_OUT.
Let's say the buffer is allocated using a page based scheme. One way to implement mmap would be to use remap_pfn_range but LDD3 says this does not work for conventional memory. It appears we can work around this by marking the page(s) reserved using SetPageReserved so that it gets locked in memory. But isn't all kernel memory already non-swappable i.e. already reserved? Why the need to set the reserved bit explicitly?
Does this have something to do with pages allocated from HIGH_MEM?
The simplest way to map a set of pages from the kernel in your mmap method is to use the fault handler to map the pages. Basically you end up with something like:
static int my_mmap(struct file *filp, struct vm_area_struct *vma)
{
vma->vm_ops = &my_vm_ops;
return 0;
}
static const struct file_operations my_fops = {
.owner = THIS_MODULE,
.open = nonseekable_open,
.mmap = my_mmap,
.llseek = no_llseek,
};
(where the other file operations are whatever your module needs). Also in my_mmap you do whatever range checking etc. is needed to validate the mmap parameters.
Then the vm_ops look like:
static int my_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
vmf->page = my_page_at_index(vmf->pgoff);
get_page(vmf->page);
return 0;
}
static const struct vm_operations_struct my_vm_ops = {
.fault = my_fault
}
where you just need to figure out for a given vma / vmf passed to your fault function which page to map into userspace. This depends on exactly how your module works. For example, if you did
my_buf = vmalloc_user(MY_BUF_SIZE);
then the page you use would be something like
vmalloc_to_page(my_buf + (vmf->pgoff << PAGE_SHIFT));
But you could easily create an array and allocate a page for each entry, use kmalloc, whatever.
[just noticed that my_fault is a slightly amusing name for a function]
Minimal runnable example and userland test
Kernel module:
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h> /* min */
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h> /* copy_from_user, copy_to_user */
#include <linux/slab.h>
static const char *filename = "lkmc_mmap";
enum { BUFFER_SIZE = 4 };
struct mmap_info {
char *data;
};
/* After unmap. */
static void vm_close(struct vm_area_struct *vma)
{
pr_info("vm_close\n");
}
/* First page access. */
static vm_fault_t vm_fault(struct vm_fault *vmf)
{
struct page *page;
struct mmap_info *info;
pr_info("vm_fault\n");
info = (struct mmap_info *)vmf->vma->vm_private_data;
if (info->data) {
page = virt_to_page(info->data);
get_page(page);
vmf->page = page;
}
return 0;
}
/* After mmap. TODO vs mmap, when can this happen at a different time than mmap? */
static void vm_open(struct vm_area_struct *vma)
{
pr_info("vm_open\n");
}
static struct vm_operations_struct vm_ops =
{
.close = vm_close,
.fault = vm_fault,
.open = vm_open,
};
static int mmap(struct file *filp, struct vm_area_struct *vma)
{
pr_info("mmap\n");
vma->vm_ops = &vm_ops;
vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
vma->vm_private_data = filp->private_data;
vm_open(vma);
return 0;
}
static int open(struct inode *inode, struct file *filp)
{
struct mmap_info *info;
pr_info("open\n");
info = kmalloc(sizeof(struct mmap_info), GFP_KERNEL);
pr_info("virt_to_phys = 0x%llx\n", (unsigned long long)virt_to_phys((void *)info));
info->data = (char *)get_zeroed_page(GFP_KERNEL);
memcpy(info->data, "asdf", BUFFER_SIZE);
filp->private_data = info;
return 0;
}
static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
struct mmap_info *info;
ssize_t ret;
pr_info("read\n");
if ((size_t)BUFFER_SIZE <= *off) {
ret = 0;
} else {
info = filp->private_data;
ret = min(len, (size_t)BUFFER_SIZE - (size_t)*off);
if (copy_to_user(buf, info->data + *off, ret)) {
ret = -EFAULT;
} else {
*off += ret;
}
}
return ret;
}
static ssize_t write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
struct mmap_info *info;
pr_info("write\n");
info = filp->private_data;
if (copy_from_user(info->data, buf, min(len, (size_t)BUFFER_SIZE))) {
return -EFAULT;
} else {
return len;
}
}
static int release(struct inode *inode, struct file *filp)
{
struct mmap_info *info;
pr_info("release\n");
info = filp->private_data;
free_page((unsigned long)info->data);
kfree(info);
filp->private_data = NULL;
return 0;
}
static const struct file_operations fops = {
.mmap = mmap,
.open = open,
.release = release,
.read = read,
.write = write,
};
static int myinit(void)
{
proc_create(filename, 0, NULL, &fops);
return 0;
}
static void myexit(void)
{
remove_proc_entry(filename, NULL);
}
module_init(myinit)
module_exit(myexit)
MODULE_LICENSE("GPL");
GitHub upstream.
Userland test:
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h> /* uintmax_t */
#include <string.h>
#include <sys/mman.h>
#include <unistd.h> /* sysconf */
/* Format documented at:
* https://github.com/torvalds/linux/blob/v4.9/Documentation/vm/pagemap.txt
*/
typedef struct {
uint64_t pfn : 54;
unsigned int soft_dirty : 1;
unsigned int file_page : 1;
unsigned int swapped : 1;
unsigned int present : 1;
} PagemapEntry;
/* Parse the pagemap entry for the given virtual address.
*
* #param[out] entry the parsed entry
* #param[in] pagemap_fd file descriptor to an open /proc/pid/pagemap file
* #param[in] vaddr virtual address to get entry for
* #return 0 for success, 1 for failure
*/
int pagemap_get_entry(PagemapEntry *entry, int pagemap_fd, uintptr_t vaddr)
{
size_t nread;
ssize_t ret;
uint64_t data;
nread = 0;
while (nread < sizeof(data)) {
ret = pread(pagemap_fd, ((uint8_t*)&data) + nread, sizeof(data),
(vaddr / sysconf(_SC_PAGE_SIZE)) * sizeof(data) + nread);
nread += ret;
if (ret <= 0) {
return 1;
}
}
entry->pfn = data & (((uint64_t)1 << 54) - 1);
entry->soft_dirty = (data >> 54) & 1;
entry->file_page = (data >> 61) & 1;
entry->swapped = (data >> 62) & 1;
entry->present = (data >> 63) & 1;
return 0;
}
/* Convert the given virtual address to physical using /proc/PID/pagemap.
*
* #param[out] paddr physical address
* #param[in] pid process to convert for
* #param[in] vaddr virtual address to get entry for
* #return 0 for success, 1 for failure
*/
int virt_to_phys_user(uintptr_t *paddr, pid_t pid, uintptr_t vaddr)
{
char pagemap_file[BUFSIZ];
int pagemap_fd;
snprintf(pagemap_file, sizeof(pagemap_file), "/proc/%ju/pagemap", (uintmax_t)pid);
pagemap_fd = open(pagemap_file, O_RDONLY);
if (pagemap_fd < 0) {
return 1;
}
PagemapEntry entry;
if (pagemap_get_entry(&entry, pagemap_fd, vaddr)) {
return 1;
}
close(pagemap_fd);
*paddr = (entry.pfn * sysconf(_SC_PAGE_SIZE)) + (vaddr % sysconf(_SC_PAGE_SIZE));
return 0;
}
enum { BUFFER_SIZE = 4 };
int main(int argc, char **argv)
{
int fd;
long page_size;
char *address1, *address2;
char buf[BUFFER_SIZE];
uintptr_t paddr;
if (argc < 2) {
printf("Usage: %s <mmap_file>\n", argv[0]);
return EXIT_FAILURE;
}
page_size = sysconf(_SC_PAGE_SIZE);
printf("open pathname = %s\n", argv[1]);
fd = open(argv[1], O_RDWR | O_SYNC);
if (fd < 0) {
perror("open");
assert(0);
}
printf("fd = %d\n", fd);
/* mmap twice for double fun. */
puts("mmap 1");
address1 = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (address1 == MAP_FAILED) {
perror("mmap");
assert(0);
}
puts("mmap 2");
address2 = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (address2 == MAP_FAILED) {
perror("mmap");
return EXIT_FAILURE;
}
assert(address1 != address2);
/* Read and modify memory. */
puts("access 1");
assert(!strcmp(address1, "asdf"));
/* vm_fault */
puts("access 2");
assert(!strcmp(address2, "asdf"));
/* vm_fault */
strcpy(address1, "qwer");
/* Also modified. So both virtual addresses point to the same physical address. */
assert(!strcmp(address2, "qwer"));
/* Check that the physical addresses are the same.
* They are, but TODO why virt_to_phys on kernel gives a different value? */
assert(!virt_to_phys_user(&paddr, getpid(), (uintptr_t)address1));
printf("paddr1 = 0x%jx\n", (uintmax_t)paddr);
assert(!virt_to_phys_user(&paddr, getpid(), (uintptr_t)address2));
printf("paddr2 = 0x%jx\n", (uintmax_t)paddr);
/* Check that modifications made from userland are also visible from the kernel. */
read(fd, buf, BUFFER_SIZE);
assert(!memcmp(buf, "qwer", BUFFER_SIZE));
/* Modify the data from the kernel, and check that the change is visible from userland. */
write(fd, "zxcv", 4);
assert(!strcmp(address1, "zxcv"));
assert(!strcmp(address2, "zxcv"));
/* Cleanup. */
puts("munmap 1");
if (munmap(address1, page_size)) {
perror("munmap");
assert(0);
}
puts("munmap 2");
if (munmap(address2, page_size)) {
perror("munmap");
assert(0);
}
puts("close");
close(fd);
return EXIT_SUCCESS;
}
GitHub upstream.
Tested on kernel 5.4.3.
Though the pages are reserved via a kernel driver, it is meant to be accessed via user space. As a result, the PTE (page table entries) do not know if the pfn belongs to user space or kernel space (even though they are allocated via kernel driver).
This is why they are marked with SetPageReserved.