This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

How do I call function in board-dm646x-evm from user space?

For my custom board derived from the DM6467T EVM, I can envision modifying board-dm646x-evm.c to include a function to do what I need.  But how do I then call, or otherwise cause this function to execute, from user space (from my application program)?

More specifically, I need to set a bit I've newly defined in Reg0 of the CPLD.   I see in board-dm646x-evm.c a couple of functions that use i2c_transfer() to read/modify/write reg0.  I even see where usb_vbus_control() is subject to EXPORT_SYMBOL() and then called elsewhere from [I believe] kernel space.  So, I figure I can clone usb_vbus_control() to make my own custom_hardware_control() function, and even export it.  But that gives me at best kernel space access to the function.  How do I get user space access?

Thanks very much,

Helmut

  • Hi,

     

    Kernel exported function can be called from the other Kernel drivers, but these functions cannot be called from the user space. To access these function, you will require to write a small kernal space module, write a small IOCTL, which will call this exported function. Now after inserting this module in the Kernel, you can open this driver and call IOCTL to configure this.

     

    The other way is to open I2c driver from the user space (/dev/i2c-0), and configure your device by caling IOCTLs provided by the I2C driver.

     

    Thanks,

    Brijesh Jadav

  • Thanks very much for your suggestions.  My research has led me to wonder about both of those two methods, but I haven't been able to get a complete picture.  I've also seen some potential problems.  Please help me further along.

    In advance, I wonder if there's a resource availability problem.  Specifically, when I run from the EVM command prompt "i2cget -y 1 0x3b", I get the error "Device or resource busy".  Will I not eventually encounter the same problem?

    Anyway, it seems like opening the I2C driver from user space might be the easiest way to handle it.  Can you possibly point to examples for how to accomplish this?  Also, isn't it /dev/i2c-1 instead of /dev/i2c-0 on the DM6467T EVM?  When I do an "ls" of the /dev folder, I only find i2c-1.

    Thanks,

    Helmut

  • Indeed, the following function far below when run can open /dev/i2c-1 successfully, but then gets "Error 16 setting I2C address: Device or resource is busy".  

    Note also, as expected, with "/dev/i2c-0" I get an error opening.  

    Note also when I try meaningless I2C address 0x3f, the code runs through without getting device busy, so the O/S must really be checking an seeing that I2C address 0x3b is already used by someone.  Therefore, I don't think opening /dev/i2c-1 is the way to go.  I think I need to call some function defined in board-dm646x-evm.c, which is I believe the culprit with control over this resource.

    -Helmut

     

    /******************************************************************************

     * NTSC_PWD(int value)

     ******************************************************************************/

    #include <linux/i2c.h>

    #include <linux/i2c-dev.h>

    //#include <asm/arch-davinci/i2c.h>

    void NTSC_PWD(int value)

    {

        // Set NTSC_PWD bit to desired value

        // Expect this to ruin video display processing, but try it for test purposes

        int fp, res;

        char *strFileName = "/dev/i2c-1";

        char buffer[2];

     

        if (0 > (fp = open(strFileName,O_RDWR))) {

            printf("Error opening %s\n",strFileName);

        } else {

            printf("Successfully opened %s\n",strFileName);

            if (0 > (res = ioctl(fp, I2C_SLAVE, 0x3b))) {

                printf("Error %d setting I2C address: %s\n",errno,strerror(errno));

            } else {

                printf("Successfully set I2C adress\n");

                buffer[0] = 0; // First byte in buffer is register address, make it 0.  Second byte in buffer is data value

                read(fp,buffer,1); // Read one data byte, contents of CPLD reg at I2C address

                printf("Read %02x from I2C\n",buffer[1]);

                if (value) {

                    buffer[1] |= 0x02; // Set bit

                } else {

                    buffer[1] &= ~0x02; // Clear bit

                }

                write(fp,buffer,1);

                printf("Bit changed\n");

            }

            close(fp);

        }

    }

  • Hi,

     

    You are correct. it is i2c-1 which is exposed as device node in /dev.

     

    I am not sure whether I2C driver supports any IOCTL to do raw read/write. I mean here read/writing device which does not have any register. Can you try looking into i2c driver documentation and see if you can find any such ioctl?

     

    As i suggested, the other way is to write a small module and write cpld register through this module.

     

    Thanks,

    Brijesh Jadav

  • The above code should work assuming that the CPLD does not need an additional register address, ie. the CPLD has exactly one register. Specifying a register address usually involves a write address/ read value combination. The problem with that is the write and read transactions are separated with a stop in between. Some devices cannot tolerate that. To create a write/read as one transaction with a restart in between, you will have to build an I2C write/read message through an ioctl call.

    Googling about, the "Device or resource busy" error is supposed to indicate that somebody else has opened the device at that address. Some sites suggest looking around this directory for more info on who has opened it:

    /sys/bus/i2c/devices

    Never checked that directory myself. Lucky enough to use I2C devices that the kernel was not interested in. Others suggest a brute force method of using I2C_SLAVE_FORCE instead of I2C_SLAVE in ioctl address spec. No really a good idea for two processes to talk to a I2C device though. Also, note 7 bit addresses. Same address for read and write.

    Another test would be to scan all addresses. Loop through addresses 0x00 to 0x7F and read a byte from each address. Hopefully one of your I2C devices is free and accessible.

  • Brijesh & Wong:

    Thanks for the help.  The problem here is strictly the "Device or resource busy".  I had considered building a kernel module, Brijesh, but I'm confident that after going through all that work, I would get the same error.  

    Someone has already "locked" I2C address 0x3a, and for good reason since it's already being used to particular purposes.  I'm interested in overloading one additional bit meaning in that register.  I need to know who has it locked and how to send a signal to that [process], which I'll correspondingly modify to respond to that process.

    I find that board-dm646x-evm.c has code accessing 0x3a, so I have already found the source I would modify, and this is likely the code that has locked the resource.  This is why I've asked elsewhere about accessing the .probe or other function, so I might build up my own parallel version.  I've also considered building up my own ioctl function.

    I will look into the /sys/bus/i2c/devices for confirmation or other info, and I'll also look into the I2C driver to see if I can glean anything.

    Thanks sincerely,

    Helmut

    Note to all, this thread is related to http://e2e.ti.com/support/embedded/f/354/p/102507/363107.aspx#363107

     

  • Okay...the I2C was a problem but not the problem. I think it all depends on what code exists for the CPLD. The CPLD's I2C must have code that has an open pointer on it. You want to use the same open pointer to access the CPLD via I2C. If your CPLD code already has a device driver interface, then you would just add another message to the ioctl handler. If you don't have a device driver interface, you will have to write your own. A character based driver can be relatively easy to implement. The point is that device driver would use the already I2C open pointer. Means modding the CPLD kernel code to expose an interface for your device driver to call.

  • Norman Wong said:

    Okay...the I2C was a problem but not the problem. I think it all depends on what code exists for the CPLD. The CPLD's I2C must have code that has an open pointer on it. You want to use the same open pointer to access the CPLD via I2C. If your CPLD code already has a device driver interface, then you would just add another message to the ioctl handler. If you don't have a device driver interface, you will have to write your own. A character based driver can be relatively easy to implement. The point is that device driver would use the already I2C open pointer. Means modding the CPLD kernel code to expose an interface for your device driver to call.

    This sounds consistent with my understanding.  In fact, it's close to exactly the same as some help I've been asking for (perhaps on another thread).  This code resides in board-dm646x-evm.c.  This code already has a function named cpld_reg0_probe(), whose address is set into a static struct i2c_driver dm6467evm_cpld_driver.probe variable.  I believe the kernel calls this function at some point.  So, I don't know if this (or other code in this .c file) establishes an API that I need to extend, or if the API I need to create will exist in parallel (and in harmony) with this .probe stuff.

    One thing I need to do for sure, is look into the ioctl stuff.  I have found http://www.linuxjournal.com/article/7353?page=0,1, which seems germane (after removing or ignoring the usb parts).  Another germane reference seems to have gone corrupt at the host: http://www.freesoftwaremagazine.com/articles/drivers_linux?page=0%2C2 entitled "The “Hello world” driver: loading and removing the driver in kernel space".

    Can you point me to a BETTER reference for figuring out how to do this?

    Thanks,

    Helmut

  • Google will give a lot of pages with the seach term "linux device driver example code". Some better that others. The O'Reilly book "Linux Device Drivers" book is somewhat disorganized but it does eventually explain what a device driver should do to play nicely in the Linux sandbox. You could hunt around your Linux distribution in the "Documentation" directory. You could also search for ".c" files with calls to either "register_chrdev" (old style) or "cdev_init" (new style). I'm enbroiled in USB work at this time and found "drivers/usb/gadget/printer.c" to be as straight-forward as Linux can be. You should be able to find a minimal driver to use as a template. Code from the internet may be written for a different version of Linux that will required tedious porting effort.

     

  • Thanks, Norman.  Note that Vaibhav Bedia has been assisting me on a different thread, where the subjects now merge here.  To all, you can see the forked history of this thread at http://e2e.ti.com/support/embedded/f/354/p/102507/363650.aspx#363650

    So now I'm interested in adding ioctl or sysfs to the board-dm646x-evm.c file, so that I can write CPLD registers.

    Please advice me a little further, based on what I see here inside board-dm646x-evm.c...

    i2c_add_driver() is called at twice, once for "dm6467evm_cpld_driver" and once for "cpld_video_driver".  I think this means TWO drivers already exist, one for each register.

    dm6467evm_cpld_driver is a structure initialized with a .driver.name "cpld_reg0", and includes an .id_table and .probe value.  The .probe is set to cpld_reg0_probe(), which is a function providing a great example of the low level code to read/modify/write the CPLD register Reg0 at I2C address 0x3a.

    cpld_video_driver is a structure initialized with a .driver.name "cpld_video", and includes a .driver.name "cpld_video", as well as .probe, .remove, and .id_table values.  The .probe is set to cpld_video_probe(), which provides further examples of how to read/modify/write a CPLD register, this time Reg1 at I2C address 0x3b.

    The text "ioctl" appears NOWHERE in the source, so I think this means neither driver has published an ioctl interface.

    One possible path for me is to ADD an ioctl interface to each of these drivers.  I believe this means I need to define a .ioctl element of some structure, and point it to a custom function I write.  But I'm not sure if such structure is already present and I need to add a line to the declaration, or if such structure needs to be added and then I need to tell the O/S about it.  I'm currently searching my Ubuntu host file system for building the kernel, for the i2c_driver structure to see if ioctl is a member...

    In addition, once I get the ioctl coded on the driver side, I need to call the ioctl from the application.  What [EDIT] name do I open before doing [/EDIT] the ioctl() function call?  Do I provide the name matching the .name.driver I quoted above, that being "cpld_reg0" or "cpld_video"?

  • (note I've edited my last paragraph above, so please reread it on forum if you're just going by what you got in email.)

  • I find in git/include/linux/i2c.h that struct i2c_driver is defined.  It doesn't have a .ioctl member, but it does had a .command member, described as "a ioctl like command that can be used to perform specific functions with the device".  This seems to be EXACTLY what I need.  So, I can go code a function and point the .command member to it.  But then, how to I invoke this from user space.  It might not be an ioctl() function call.  Please suggest if you know.  Meanwhile, I'll search further for examples using it.  (The word "command" is awfully darned generic. The search is going to be difficult.)

    EDIT: git/Documentation/i2c/writing-clients says that ".command" is deprecated.  But no alternate is provided!  So I guess I'm going to ignore this deprecation...

  • Drivers "dm6467evm_cpld_driver" and once for "cpld_video_driver" are definitely drivers private to the kernel and do not create a user space device driver file, ie. in "/dev". The file board-dm646x-evm.c has two global variables cpld_reg0_client and cpld_client. These could be used for external APIs that would be called from either sysfs or devfs. I haven't used sysfs. The GPIO subsytem uses the sysfs, eg "/sys/class/gpio". It's fairly complicated at "drivers/gpio". There are probably less complicated examples. A device driver in the devfs is more common. Hard to describe the overall process. Here's a much simplified peudo-code EDIT:Attached as file. Forum SW not behaving with formatting.
  • I am still looking for a solution to this difficulty. I've read your recent post, Norman, but forgot to look at your attachment. I'll do that the next chance I get.

    In the mean time, I've accomplished one of my two objectives per my new post "How to shut down PHY to save power, DM6467T EVM" at http://e2e.ti.com/support/embedded/f/354/p/103662/364531.aspx#364531

  • Norman Wong said:
    Drivers "dm6467evm_cpld_driver" and once for "cpld_video_driver" are definitely drivers private to the kernel and do not create a user space device driver file, ie. in "/dev". The file board-dm646x-evm.c has two global variables cpld_reg0_client and cpld_client. These could be used for external APIs that would be called from either sysfs or devfs. I haven't used sysfs. The GPIO subsytem uses the sysfs, eg "/sys/class/gpio". It's fairly complicated at "drivers/gpio". There are probably less complicated examples. A device driver in the devfs is more common. Hard to describe the overall process. Here's a much simplified peudo-code EDIT:Attached as file. Forum SW not behaving with formatting.

    The visual image of how to accomplish what I need finally developed in my mind.  At the same time, I realized I needed to come back and check our your attachment.  Lo and behold your attachment is a roadmap for implementing my visual image!  Of course, I might have gotten here faster by looking at your attachment earlier.  Nevertheless, I still needed to get the image in my mind as well.  With BOTH in hand now (yes, I'm holding my head in my hands while I sob... no, that was last week), I'll start work on it and let you know what happens.

    Thanks for the help!

    -Helmut

  • Question: In printer.c to which you referred, only ONE minor number is allocated with the call "alloc_chrdev_region(&g_printer_devno, 0, 1, "USB printer gadget")", yet later TWO device numbers are unregistered with the call "unregister_chrdev_region(g_printer_devno, 2)".  Of course, unregister_chrdev_region is called elsewhere with a 1.  So I'm a little confused.  I'm going to start with a 1, but please correct me if it needs to be a 2.  (I realize minor number and device number might not be the same thing, but the function names imply they should be working with the same thing.

    Thanks,

    Helmut

  • ...also, for my ioctl commands, I'm using something like:

    #define CPLDREGS_IOCTL_DISABLE_INTERNET_PHY _IOWR('r', 0x71, unsigned char)
    #define CPLDREGS_IOCTL_ENABLE_INTERNET_PHY _IOWR('r', 0x71, unsigned char)
    #define CPLDREGS_IOCTL_VIDEO_PASSTHROUGH _IOWR('r', 0x71, unsigned char)
    #define CPLDREGS_IOCTL_VIDEO_PROCESS _IOWR('r', 0x71, unsigned char) 

    But comments in printer.c suggest to me that these ought to be unique.  For example, how do I know if someone else is using 'r', and how do I make sure 0x7X doesn't conflict with theirs?

    Thanks,

    Helmut

     

  • ...and one more thing, please.  How do I get this driver into the kernel?

    For the moment, I've added all my code to the bottom of board_dm646x-evm.c, that I had to change for other reasons anyway.  But I don't imagine its presence there will cause my __init function to get called, etc.  I'm a little hesitant of doing it in a fully separate manner.

    I hope it's not like some other things, and merely by the presence of a .c file in a folder.  (I hate that.)

    There's also the .config file that I've changed.  I don't know if I have to add my one "CONFIG_CPLDREGS=y" and then continue making changes down the line.

    ???

    I'm searching git right now for CONFIG_USB_G_PRINTER to see how this printer driver example gets into the kernel...  Well, the only non-defconfig use is in the gadget Makefile, where all it does it cause inclusion of the printer.o.  Darn, it *is* one of those "merely by the presence of" things.  Is it the case that merely linking in the .o, the driver __init function will end up getting called?  If so, then it ought to work to simply leave my code in board_dm646x-evm.c.  Maybe I'll test it this way, then later separate it into a complete folder akin to git/drivers/thermal.  But then again, this is HIGHLY hardware dependent.  Some davinci folder or simply inside board_dm646x-evm.c seems appropriate...

    -Helmut

  • ...well, with the code in board-dm646x-evm.c, when I install the kernel, boot, and login, I do find cpldregs under both /sys and /dev.  This suggests it worked.  I did not, however, see my printk output during the boot that I expected to see.  I was using KERN_NOTICE.  Maybe I'll retry with KERN_CRIT and see if they come out...  Nope, my printk is still not coming out.  But the /sys and /dev files are there???  I'll try testing the ioctl with a user space application tomorrow...

  • The alloc of 1 number and free of 2 numbers is quite probably a bug. That's Linux code for you.

    The Linux Device Drivers is online at http://lwn.net/Kernel/LDD3/. Chapter 6 is probably most useful to you.

    The ioctrl magic numbers are a bit of a good intention and I am not sure that they are even checked. Your driver will receive all ioctl messages even it they do not have the correct number. I don't think the ioctrl code can know. The "official" numbers are at Documention/ioctl-number.txt. I've used straight numbers unencumbered by direction and magic numbers in the past. But best to stay with the Linux way just in case the kernel writers enforce it later.

    The init and cleanup functions automatically get called when you boot or load the module. The special attibute __init places the function address in a section that the gets called on boot. For built-in modules, I think you would just add your new "your_file.o" to the same list as board-dm646x.o in the makefile. Modifying the kernel .config file can be dangerous. That file is auto-generated and is deleted on distcleans. You would have to modify the Kconfigs. Not easy. And a new release of the kernel would not have your changes.

    Kernel modules can also be loaded at runtime. Modules can be built within the kernel or outside of it. Development can be faster with loadable modules as only the module is compiled each time. Loadable modules are also the choice for keeping proprietary modules out of GPL. Also avoid messing with kernel build system. Setting up an external make is not too hard. It's documented in the online book mentioned above.

    Just a note warning though about a makefile for a loadable module external to the kernel. It will do a two pass make. One for the local directory and another pass in the context of the kernel.

     

  • RESOLVED.

    Brijesh and Norman, thanks for your help.  Norman, excellently crafted post just above (begins with "The alloc...").

    For my solution, see:

    This is how to read/write CPLD registers on DM6467T http://e2e.ti.com/support/embedded/f/354/p/104258/366748.aspx#366748