Communicating with hardware ldd3 Study Notes

Source: Internet
Author: User

Communicating with hardware



Using I/O Ports


I/O ports are the means by which drivers communicate with custom devices, at least part of the time. this section covers the varous functions available for making use of I/O Ports; we also touch on some portability issues.


I/O port allocation


As you might custom CT, you shocould not go off and start pounding on I/O ports without first ensuring that you have exclusive access to those ports. the kernel provides a registration interface that allows your driver to claim the ports it needs. the core function in that interface is request_region:

#include <linux/ioport.h>struct resource *request_region(unsigned long first, unsigned long n, const char *name);


This function tells the kernel that you wowould like to make use of N ports, starting with first. the name parameter shocould be the name of your device. the return value is non-null if the allocation succeeds. if you get null back from request_region, you will not be able to use the desired ports.

All port allocations show up in/proc/ioports. if you are unable to allocate a needed set of ports, that is the place to look to see who got there first. when you are done with a set of I/O Ports (at module unload time, perhaps), they shoshould be returned to the system:

void release_region(unsigned long start, unsigned long n);


There is also a function that allows your driver to check to see whether a given set of I/O ports is available:


int check_region(unsigned long first, unsigned long n);


Here, the return value is a negative error code if the given ports are not available. this function is deprecated because its return value provides no guarantee of whether an allocation wocould succeed; checking and later allocating are not an atomic operation. we list it here because several drivers are still using it, but you should
Always use request_region, which performs the required locking to ensure that the allocation is done in a safe, atomic manner.



I/O memory allocation and Mapping


I/O memory regions must be allocated prior to use. The interface for allocation of memory regions (defined in <Linux/ioport. h>) is:


struct resource *request_mem_region(unsigned long start, unsigned long len, char *name);


This function allocates a memory regionLenBytes, starting at start. If all goes well, a non-null pointer is returned; otherwise the return value is null. All I/O memory allocations are listed in/proc/iomem.

Memory regions shoshould be freed when no longer needed:

void release_mem_region(unsigned long start, unsigned long len);

There is also an old function for checking I/O memory region availability:

int check_mem_region(unsigned long start, unsigned long len);


But, as with check_region, this function is unsafe and shoshould be avoided.

Allocation of I/O memory is not the only required step before that memory may be accessed. you must also ensure that this I/O memory has been made accessible to the kernel. getting at I/O memory is not just a matter of dereferencing a pointer; on Alibaba systems, I/O memory is not directly accessible in this way at all. so a mapping must be set up first.





Compiler Optimization may cause the operation to access the IO port to be optimized...

Solution:

The solution to compiler optimization and hardware reordering is to place a memory barrier between operations that must be visible to the hardware (or to another processor) in a particle order. linux provides four macros to cover all possible ordering needs:


#include <linux/kernel.h>void barrier(void)


This function tells the compiler to insert a memory barrier but has no effect on the hardware. compiled code stores to memory all values that are currently modified and resident in CPU registers, and rereads them later when they are needed. A call to barrier prevents compiler optimizations within ss the barrier but leaves the hardware free to do its own reordering.



Manipulating I/O Ports


After a driver has requested the range of I/O ports it needs to use in its activities, it must read and/or write to those ports. to this end, most hardware differentiates between 8-bit, 16-bit, and 32-bit ports. usually you can't mix them like you normally do with system memory access. *

A c program, therefore, must call different functions to access different size ports. as suggested in the previous section, computer ubuntures that support only memory-mapped I/O registers fake port I/O by remapping port addresses to memory addresses, and the kernel hides the details from the driver in order to export portabil-
Ity.

The Linux kernel headers (specifically, the architecture-dependent header <ASM/IO. h>) define the following inline functions to access I/O ports:


unsigned inb(unsigned port);void outb(unsigned char byte, unsigned port);Read or write byte ports (eight bits wide). The port argument is defined asunsigned long for some platforms and unsigned short for others. The returntype of inb is also different across architectures.unsigned inw(unsigned port);void outw(unsigned short word, unsigned port);

These functions access 16-bit ports (one word wide); they are not available when compiling for the s390 platform, which supports only byte I/O.
unsigned inl(unsigned port);void outl(unsigned longword, unsigned port);



Here is a simple demo of communication with hardware:


Http://blog.csdn.net/cinmyheart/article/details/38136375

Use a character device to drive and control the LED light (because it is only a simple demo and does not consider the issue of resource preemption, we will provide a complete version below)


/**************************************************************code writer: EOFcode date: 2014.08.26code file<span style="white-space:pre"></span>: led_by_EOF.ce-mail: [email protected]code purpos:This code is a demo for beginner how to write acharacter device to drive IO port.If you find there is something wrong with my codeand change it into a better version , please touch me by e-mail. Thank you.**************************************************************/#include <linux/init.h>#include <linux/slab.h>#include <linux/fs.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/kernel.h> /* for 'printk()'*/#include <asm/uaccess.h> /* for 'copy_from_user()' */#include <linux/cdev.h>#include <linux/kdev_t.h>#include <linux/types.h>#include <mach/gpio-bank-m.h>#include <mach/regs-gpio.h>#include <mach/map.h>#include <linux/pci.h>#include <linux/device.h>#include <linux/delay.h> /* for 'msleep()' */MODULE_AUTHOR("EOF");MODULE_LICENSE("Dual BSD/GPL");#define DEVICE_NAME"led_by_EOF"#define DEVICE_MAJOR_NUMBER 0 #define PORT_NUM3#define USE_IMMEDIATEstatic dev_t dev_number = DEVICE_MAJOR_NUMBER;static struct class* led_class;static struct cdev my_led_cdev;static int led_open(struct inode* node,struct file* file){printk(KERN_ALERT "Device Opened successful!\n");return 0;}static ssize_t led_write(struct file* file,const char __user* buf,size_t count,loff_t* ppos){int kbuf;int ret = 0;kbuf = readl(S3C64XX_GPMCON);kbuf &= (~0xFFFF);kbuf |= 0x1111;writel(kbuf,S3C64XX_GPMCON);printk(KERN_ALERT "before writing...  &kbuf:%p buf:%p\n",&kbuf,buf);ret = __copy_from_user(&kbuf,buf,count);if(ret != 0){printk(KERN_ALERT "'__copy_from_user' failed! ret:%d\n",ret);return -1;}printk(KERN_ALERT "writing...   kbuf:%x \n",kbuf);writel(kbuf,S3C64XX_GPMDAT);return 0;}static int led_release(struct inode* inode,struct file*file){printk(KERN_ALERT "Device released\n");return 0;}static struct file_operations led_fops ={.owner=THIS_MODULE,.open=led_open,.write=led_write,.release=led_release,};int led_init(void){int kbuf;/* Attention!** If you want to use ports that you wanted, ** you should request them firstly.*/if(!request_region((unsigned long)S3C64XX_GPM_BASE,PORT_NUM,DEVICE_NAME)){printk(KERN_ALERT "led_by_EOF : can't get I/O port address %x\n",(unsigned int)S3C64XX_GPM_BASE);return -ENODEV;}/* Here we register our device - should not failed thereafter */if(alloc_chrdev_region(&dev_number,0,1,DEVICE_NAME) < 0){printk(KERN_ALERT "Error in function '%s' : can't register device\n",__FUNCTION__);return -1;}led_class = class_create(THIS_MODULE,DEVICE_NAME);if(IS_ERR(led_class)){printk(KERN_ALERT "Bad class create\n");return -1;}cdev_init(&my_led_cdev,&led_fops);/*** GPM0~GPM3 pull up*/kbuf = readl(S3C64XX_GPMPUD);kbuf &= (~0xFF);kbuf |= 0xaa;//1010 1010writel(kbuf,S3C64XX_GPMPUD);/*** GPM0~3 output mode*/kbuf = readl(S3C64XX_GPMCON);kbuf &= (~0xFFFF);kbuf |= 0x1111;writel(kbuf,S3C64XX_GPMCON);/***GPM0~GPM3 output 0 and light up all LED*/kbuf  = __raw_readl(S3C64XX_GPMDAT);kbuf |= 0x10;writel(kbuf,S3C64XX_GPMDAT);if(cdev_add(&my_led_cdev,dev_number,1)){printk(KERN_ALERT "Bad cdev add\n");return 1;}device_create(led_class,NULL,dev_number,NULL,DEVICE_NAME);return 0;}void led_cleanup(void){device_destroy(led_class,MKDEV(MAJOR(dev_number),0));class_destroy(led_class);unregister_chrdev_region(MAJOR(dev_number),1);/* release the ports that we allocated */release_region((unsigned long)S3C64XX_GPM_BASE,PORT_NUM);printk(KERN_ALERT "See you! My LED\n");}module_init(led_init);module_exit(led_cleanup);











Communicating with hardware ldd3 Study Notes

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.