Linux I/O port programming mini-HOWTO

Author: Riku Saikkonen v3.0, 2000-12-13

This HOWTO document describes programming hardware I/O ports and waiting for small periods of time in user-mode Linux programs running on the Intel x86 architecture.

===========================================

linux并口驱动代码有问题,帮忙看看。

重新启动系统以后加载概模块,提示说0x378端口已经被占用,所以我卸载了lp 和 parport_pc模块,可以加载成功,可是我卸载以后再重新加载不成功,又说资源被占用。可能是卸载模块时没有释放资源,可是代码里面明明有这一部分代码阿???郁闷中。。。。。。。

卸载模块时提示:

Trying to free nonexistent resource <00000378-0000037f>

代码如下:

#ifndef __KERNEL__

# define __KERNEL__

#endif

#ifndef MODULE

# define MODULE

#endif

//#include

#include

#include

#include

#include

#include

#include /* printk() */

#include /* everything... */

#include /* error codes */

#include /* udelay */

//#include

#include

#include

#include

#include

#include

#include

#ifdef MODULE_LICENSE

MODULE_LICENSE ("GPL");

#endif

static unsigned long base = 0x378;

unsigned long short_base = 0x378;

static unsigned long major = 0;

static int portnum = 8;

int

short_open (struct inode *inode, struct file *filp)

{

MOD_INC_USE_COUNT;

return 0;

}

int

short_release (struct inode *inode, struct file *filp)

{

MOD_DEC_USE_COUNT;

return 0;

}

ssize_t

do_short_write (struct inode * inode, struct file * filp, const char *buf,

size_t count, loff_t * f_pos)

{

int retval = count;

unsigned long address = short_base + 2;

unsigned char *kbuf = kmalloc (count, GFP_KERNEL), *ptr;

if (!kbuf)

return -ENOMEM;

if (copy_from_user (kbuf, buf, count))

return -EFAULT;

ptr = kbuf;

while (count--)

{

outb (*(ptr++), address);

wmb ();

}

kfree (kbuf);

return retval;

}

ssize_t

do_short_read (struct inode * inode,

struct file * filp, char *buf, size_t count, loff_t * f_pos)

{

int retval = count;

unsigned long address = short_base + 2;

unsigned char *kbuf = kmalloc (count, GFP_KERNEL), *ptr;

if (!kbuf)

return -ENOMEM;

ptr = kbuf;

while (count--)

{

*(ptr++) = inb (address);

rmb ();

}

if ((retval > 0) && copy_to_user (buf, kbuf, retval))

retval = -EFAULT;

kfree (kbuf);

return retval;

}

ssize_t

short_read (struct file * filp, char *buf, size_t count, loff_t * f_pos)

{

return do_short_read (filp->f_dentry->d_inode, filp, buf, count, f_pos);

}

ssize_t

short_write (struct file * filp, const char *buf, size_t count, loff_t * f_pos)

{

return do_short_write (filp->f_dentry->d_inode, filp, buf, count, f_pos);

}

struct file_operations short_fops = {

read:short_read,

write:short_write,

open:short_open,

release:short_release,

};

int

init_module (void)

{

int portnum = 1;

int result;

SET_MODULE_OWNER (&short_fops);

result = check_region (base, portnum);

if (result)

{

printk (KERN_INFO "short: can't get I/O port address 0x%lx ",

short_base);

return result;

}

request_region (base, portnum, "short");

result = register_chrdev (major, "short", &short_fops);

if (result < 0)

{

printk (KERN_INFO "short: can't get major number ");

release_region (short_base, portnum);

return result;

}

if (major == 0)

major = result;

return 0;

}

void

cleanup_module (void)

{

printk ("major = 0x%lx ", major);

unregister_chrdev (major, "short");

release_region (base, portnum);

}

编译:

gcc -D__KERNEL__ -O2 -Wall -I/usr/src/linux-$(shell uname -r)/include -c pell.c

------------------------

已经找到原因,这是你的一个小小的笔误,就是你可以查看一下你在

init_module中有这么一句话

int portnum = 1;

而你在前面定义了一个全局变量

static int portnum = 8;

这样在编译器处理的时候就会把portnum写成1,这样当你用release_region释放资源的时候就会发现没有 合适的资源要释放

这是因为你这时要释放的是8个字节长的resource,而你前面申请的只有1个byte长的resource ,这样就会造成资源泄漏.

谢谢你提供了源代码,不然,真的没有人能够找出这其中的毛病,

------------------开放就意味着找出更多的不足,得到更大的改进.

============================================

两种方式

一  驱动程序执行方式

1.申请I/O端口

A 直接端口方式

check_region

request_region

B内存映射方式

check_mem_region

request_mem_retion

然后队端口地址映射

ioremap

2.注册驱动

register_chrdev  申请主设备号,注册驱动名,相关的操作.

3.探测中断

A 内核探测

B 定制探测

C 直接根据I/O地址,分配相应的IRQ号

4.安装中断相应处理函数

request_irq(irq,(*)Handler()...)

A 共享中断的处理函数

端口有中断到,则判断时不时本端口的中断.若是.则填充缓冲区.同时wake_up_interruptible,唤起等待序列.

在read操作中的队列由signal_pending唤醒,拷贝数据到用户空间.

B 下半部中断的处理函数

通过queue_task,执行队列处理函数唤起中断.

C  小任务中断处理函数

通过tasklet_schedule执行小任务处理函数唤起中断.

二   应用程序执行方式.通过ioperm命令,例如,ioperm ( BASE, range , 1),调用内核,得到 I/O 地址空间的使用权;

通过一个发送请求指令,例如,outb(1, BASE ),

等待足够的时间让咖啡煮好,让时间参数在命令行中被读取是一件很好的事情

然后发送 out(0, BASE) 指令关掉咖啡机

在结束之前还应归还并口 I/O 地址的使用权,ioperm(BASE,range,0) .     程序:/* coffee.c */

#include /* linux-specific */

#ifdef __GLIBC__

#  include

#endif

int main(int argc, char **argv)

{

setuid(0); /* if we're setuid, force it on */

if(ioperm(0x378,1,1))

printf("error,we can't  ioperm our ox378 port\n");

outb(0xff,0x378);

sleep(5);

outb(0,0x378);

if(ioperm(0x378,1,0))

printf("error,we can't  ioperm our ox378 port\n");

exit(1);

}

三   在驱动程序中如何取得被其它驱动程序使用的并口

首先   cat   /proc/ioports  看端口地址是分配给谁了.

然后看该名字的 ls -l  /dev/port_name

找到主设备号和次设备号

然后  cat   /proc/devices 看主设备好对应的驱动

然后rmmod 该驱动.

如果是编译到了内核的驱动则只需在/lib/module/`uname   -r`/kernel/drivers/ 删除该名字的驱动,

则重起后,该驱动就不会暂用该端口了,但这个驱动的名字不一定在/proc/devices 中出现

=============================================

Linux并口网络解决方案(转贴)

2003-04-10 nesta1

大家知道,在DOS环境下,我们可以用并口或串口将两台PC连接起来,一台充当服务器,另一台充当客户,但充当服务器的机器不能做其它操作,只能为 Client服务。虽然在方便上和速度均不如网卡,但它提供了一个“穷人”的解决方案。如果仅拷贝少量数据,它还是可以满足一般人的需求。并口的速度要远远比串口快。

在Linux内核中,网络设备中有一个叫PLIP (Parallel Line Internet Protocol). 它提供了并口的网络支持,并将并口映射成网络设备。它支持标准并口,扩展并口的支持。传送速度依赖于并口线的质量和机器的配置。

下面,我将系统的配置作简要介绍。

1. 在内核中支持PLIP

cd /usr/src/linux

make menuconfig

# select Network device support

select PLIP as modules

2. 编译内核

cd /usr/src/linux

make dep; make bzImage; make modules; make modules_install;

3. 将新内核配置到Lilo中去。

4. 重新启动

5. 打开Ipforward.

echo 1 > /proc/sys/net/ipv4/ip_forward

6. 运行网络配置工具,配置PLIP

turbonetcfg

# add new interface

# select PLIP

# add the ipaddress and mask to that

# save & exit

7. 启动PLIP

modprobe plip

# if it does not work

# echo 7 > /proc/parport/0/irq

# modprobe plip

ifup plip0

8. 配置网关。

# eg. Machine A:

# PLIP0 -- 10.0.0.1

#B -- gateway

# PLIP0 -- 10.0.0.2

# eth0 -- 172.16.69.12

# in B Machine, we setup ipchains

/sbin/ipchains -A forward -j MASQ -s 10.0.0.0/255.255.255.0 -d 0.0.0.0/0

通过我们的试验,使用WWW, TELNET 与普通网卡没有区别,但当使用FTP时,速度稍慢,大约35K/s

左右,并且在FTP的同时,我们也发现系统偶尔出现Timeout, 并且当你作其它事情时,感到系统很慢。说

明PLIP的驱动程序还需要改进。

参考资料:/usr/src/linux/Documentation/networking/PLIP.txt

===============================================

9.3. 一个 I/O 端口例子

我们用来展示一个设备驱动内的端口 I/O 的例子代码, 操作通用的数字 I/O 端口; 这样的端口在大部分计算机系统中找到.

一个数字 I/O 端口, 在它的大部分的普通的化身中, 是一个字节宽的 I/O 位置, 或者内存映射的或者端口映射的. 当你写一个值到一个输出位置, 在输出管脚上见到的电信号根据写入的单个位而改变. 当你从一个输入位置读取一个值, 输入管脚上所见的当前逻辑电平作为单个位的值被返回.

这样的 I/O 端口的实际实现和软件接口各个系统不同. 大部分时间, I/O 管脚由 2 个 I/O 位置控制: 一个允许选择使用那些位作为输入, 哪些位作为输出, 以及一个可以实际读或写逻辑电平的. 有时, 但是, 事情可能更简单, 并且这些位是硬连线为输入或输出(但是, 在这个情况下, 它们不再是所谓的"通用 I/O"); 在所有个人计算机上出现的并口是这样一个非通用 I/O 端口. 任一方式, I/O 管脚对我们马上介绍的例子代码是可用的.

9.3.1. 并口纵览

因为我们期望大部分读者以所谓的"个人计算机"的形式使用一个 x86 平台, 我们觉得值得解释一下 PC 并口如何设计的. 并口是在个人计算机上运行数字 I/O 例子代码的外设接口选择. 尽管大部分读者可能有并口规范用, 为你的方便, 我们在这里总结一下它们.

并口, 在它的最小配置中 ( 我们浏览一下 ECP 和 EPP 模式) 由 3 个 8-位端口组成. PC 标准在 0x378 开始第一个并口的 I/O 端口并且第 2 个在 0x278. 第一个端口是一个双向数据寄存器; 它直接连接到物理连接器的管脚 2 - 9. 第 2 个端口是一个只读状态寄存器; 当并口为打印机使用, 这个寄存器报告打印机状态的几个方面, 例如正在线, 缺纸, 或者忙. 第 3 个端口是一个只出控制寄存器, 它, 在其他东西中, 控制是否中断使能.

并口通讯中使用的信号电平是标准的 TTL 电平: 0 和 5 伏特, 逻辑门限在大概 1.2 伏特. 你可依靠端口至少符合标准 TTL LS 电流规格, 尽管大部分现代并口在电流和电压额定值都工作的好.

并口连接器和计算机内部电路不隔离, 当你想直接连接逻辑门到这个端口是有用的. 但是你不得不小心地正确连接线; 并口电路当你使用你自己的定制电路时容易损坏, 除非你给你的电路增加绝缘. 你可以选择使用插座并口如果你害怕会损坏你的主板.

位的规范在图 并口的管脚 中概述. 你可以存取 12 个输出位和 5 个输入位, 有些是在它们地信号路径上逻辑地翻转了. 唯一的没有关联信号管脚的位是端口 2 的位 4 (0x10), 它使能来自并口的中断. 我们使用这个位作为我们的在第 10 章中的中断处理的实现的一部分.

图 9.1. 并口的管脚

9.3.2. 一个例子驱动

我们介绍的驱动称为 short (Simple Hardware Operations and Raw Tests). 所有它做的是读和写几个 8-位 端口, 从你在加载时选择的开始. 缺省地, 它使用分配给 PC 并口的端口范围. 每个设备节点(有一个独特的次编号)存取一个不同的端口. short 驱动不做任何有用的事情; 它只是隔离来作为操作端口的单个指令给外部使用. 如果你习惯端口 I/O, 你可以使用 short 来熟悉它; 你能够测量它花费来通过端口传送数据的时间或者其他游戏的时间.

为 short 在你的系统上运行, 必须有存取底层硬件设备的自由(缺省地, 并口); 因此, 不能有其他驱动已经分配了它. 大部分现代发布设置并口驱动作为只在需要时加载的模块, 因此对 I/O 地址的竞争常常不是个问题. 如果, 但是, 你从 short 得到一个"无法获得 I/O 地址" 错误(在控制台上或者在系统 log 文件), 一些其他的驱动可能已经获得这个端口. 一个快速浏览 /proc/ioports 常常告诉你哪个驱动在捣乱. 同样的告诫应用于另外 I/O 设备如果你没有在使用并口.

从现在开始, 我们只是用"并口"来简化讨论. 但是, 你能够设置基本的模块参数在加载时来重定向 short 到其他 I/O 设备. 这个特性允许例子代码在任何 Linux 平台上运行, 这里你对一个数字 I/O 接口有权限通过 outb 和 inb 存取( 尽管实际的硬件是内存映射的, 除 x86 外的所有平台). 后面, 在"使用 I/O 内存"的一节, 我们展示 short 如何用来使用通用的内存映射数字 I/O.

为观察在并口上发生了什么以及如果你有使用硬件的爱好, 你可以焊接尽管 LED 到输出管脚. 每个 LED 应当串连一个 1-K 电阻导向一个地引脚(除非, 当然, 你的 LED 有内嵌的电阻). 如果你连接一个输出引脚到一个输入管脚, 你会产生你自己的输入能够从输入端口读到.

注意, 你无法只连接一个打印机到并口并且看到数据发向 short. 这个驱动实现简单的对 I/O 端口的存取, 并且没有进行与打印机需要的来操作数据的握手; 在下一章, 我们展示了一个例子驱动(称为 shortprint ), 它能够驱动并口打印机; 这个驱动使用中断, 但是, 因此我们还是不能到这一点.

如果你要查看并口数据通过焊接 LED 到一个 D-型 连接器, 我们建议你不要使用管脚 9 和管脚 10, 因为我们之后连接它们在一起来运行第 10 章展示的例子代码.

只考虑到 short, /dev/short0 写到和读自位于 I/O 基地址的 8-bit 端口( 0x378, 除非在加载时间改变). /dev/short1 写到位于基址 + 1 的 8-位, 等等直到基址 + 7.

/dev/short0 进行的实际输出操作是基于使用 outb 的一个紧凑循环. 一个内存屏障指令用来保证输出操作实际发生并且不被优化掉:while (count--) {

outb(*(ptr++), port);

wmb();

}

你可以运行下列命令来点亮你的 LED:echo -n "any string" > /dev/short0

每个 LED 监视一个单个的输出端口位. 记住只有最后写入的字符, 保持稳定在输出管脚上足够长时间你的眼睛能感觉到. 因此, 我们建议你阻止自动插入一个结尾新行, 通过传递一个 -n 选项给 echo.

读是通过一个类似的函数, 围绕 inb 而不是 outb 建立的. 为了从并口读"有意义的"值, 你需要某个硬件连接到连接器的输入管脚来产生信号. 如果没有信号, 你会读到一个相同字节的无结尾的流. 如果你选择从一个输出端口读取, 你极可能得到写到端口的最后的值(这适用于并口和普通使用的其他数字 I/O 电路). 因此, 那些不喜欢拿出他们的烙铁的人可以读取当前的输出值在端口 0x378, 通过运行这样一个命令:dd if=/dev/short0 bs=1 count=1 | od -t x1

为演示所有 I/O 指令的使用, 每个 short 设备有 3 个变形: /dev/short0 进行刚刚展示的循环, /dev/short0p 使用 outb_p 和 inb_p 代替"快速"函数, 并且 /dev/short0s 使用字串指令. 有 8 个这样的设备, 从 short0 到 short7. 尽管 PC 并口只有 3 个端口, 你可能需要它们更多如果使用不同的 I/O 设备来运行你的测试.

short 驱动进行一个非常少的硬件控制, 但是足够来展示如何使用 I/O 端口指令. 感兴趣的读者可能想看看 parpor 和 parport_pc 模块的源码, 来知道这个设备在真实生活中能有多复杂来支持一系列并口上的设备(打印机, 磁带备份, 网络接口)

linux如何加载并口驱动程序,Linux下并口的访问★★★★★相关推荐

  1. linux 开机加载的文档,Linux 修改文档“打开方式”

    修改默认打开程序 多种方法: 使用系统Details程序修改( System > Details > Default Applications),仅可以修改少数程序: Paste_Imag ...

  2. Linux可加载内核模块(LKM)(转载)

    转载: 漏天剑 文章导航: 为校长杯流尽最后一滴血, Lkm注射, 返回首页 Linux可加载内核模块(LKM) Linux可加载内核模块完全版 --黑客.病毒程序编写者和系统管理员的概念性指南 作者 ...

  3. linux驱动加载流程分析

    linux驱动加载流程分析 内核是如何加载驱动的,有些是编译到内核里面,有些事编译成ko,让系统自动加载.总的说来,在Linux下可以通过两种方式加载驱动程序:静态加载和动态加载. 静态加载就是把驱动 ...

  4. linux系统加载内核模块的命令是,linux insmod命令载入模块内核

    功能描述 Linux 有许多功能是通过模块的方式,在需要时才载入 kernel.如此可使 kernel 较为精简,进而提高效率,以及保有较大的弹性.这类可载入的模块,通常是设备驱动程序. [insmo ...

  5. Linux驱动模块加载和卸载以及设备注册与注销

    一.驱动模块的加载和卸载 Linux驱动有两种运行方式,第一种就是将驱动编译进Linux内核中,这样当Linux内核启动的时候就会自动运行驱动程序.第二种就是将驱动编译成模块(Linux下模块扩展名为 ...

  6. linux 内核加载错误 Invalid module format

    遇到这种情况后,通过 dmesg 看一下内核日志. 如果发现有如下日志,那就好办了. hello: version magic '2.6.33.3 ' should be '2.6.33.3-85.f ...

  7. Linux模块加载流程及如何让系统开机自动加载模块

    Linux模块加载 Linux系统加载哪些内核模块,和配置文件有关系. 模块保存在/lib/modules/下. 使用/etc/modules-load.d/来配置系统启动时加载哪些模块. 使用/et ...

  8. 备份linux分区到usb,将Linux引导加载程序备份到USB的方法

    本文介绍如何将Linux引导加载程序备份到USB以应对紧急情况.大多数Linux操作系统使用的引导加载程序称为Grub,这是一个简单的工具,可以配置为在PC启动时从菜单启动Linux发行版以及其他操作 ...

  9. LINUX配置文件加载顺序

    LINUX配置文件加载顺序 shell启动方式 启动类型 如何判断是否为交互式 Shell? 有两种方式 判断是否为登录式 Shell ? 同时判断交互式和登录式 配置文件启动 配置文件加载说明 启动 ...

  10. Linux如何加载与卸载U盘

    Linux如何加载与卸载U盘   第一:以root用户登陆 先加载USB模块 modprobe usb-storage 用fdisk -l 看看U盘的设备 假如U盘是sda1   第二:确定在 目录 ...

最新文章

  1. hdu 3046(最小割)
  2. 《CCNP SWITCH 300-115学习指南》——导读
  3. 【设计模式】Spring的核心IOC容器中用到的设计模式
  4. 曲线均匀分布_R——概率统计与模拟(三) 变换均匀分布对特定分布进行抽样
  5. zabbix icmp ping 监控主机_Zabbix监控 MySQL 性能
  6. C# v7.0版本中的local function
  7. opencv之retina滤波器---Retina_create
  8. python数据分析-如何在业余时学数据分析?
  9. 声网 java app视频录制_java 声音的播放与录制
  10. python newbie——PE No.3
  11. indesign缩放图片被切割_InDesign怎么改变图片的大小
  12. canvas实现动态小球碰撞
  13. glnxa64 matlab 什么版本_Matlab 2014a 免费版-Matlab2014 Mac版下载 V2014b免费版-PC6苹果网...
  14. 利用 Chrome 的打印功能将网页保存为 PDF
  15. 使用超级鹰模拟登录验证码报错的解决办法
  16. 手机浏览器能上网微信无法连接服务器,手机打不开微信的网页怎么办?手机打不开微信网页的原因和解决方法...
  17. (python 3)1040 有几个PAT (25分)
  18. git log 详解
  19. 如何下载酷6、土豆、优酷、56视频并转化格式进行播
  20. 商业智能助力 银行业数据“挖金”

热门文章

  1. (转)求质数算法的N种境界[1] - 试除法和初级筛法
  2. html给按钮加颜色代码,css按钮属性 html中按钮的字体颜色怎么设置?
  3. 设计模式(创建型)之建造者模式(Builder Pattern)
  4. [游泳] 学习蛙泳过程用到的一些资料
  5. c语言isfinite_inf是什么意思,C语言中INF和NAN是什么意思
  6. 看到一片好文章,同享
  7. 特别手持弹幕 技术支持 app's support
  8. 最简单的C语言编程:快速入门a的n次方
  9. sql server存储过程修改,存储到mysql笔记
  10. 数据分析/运营——常用EXCEL函数(IF、SUMIF、VLOOKUP)