Chinaunix首页 | 论坛 | 博客
  • 博客访问: 363200
  • 博文数量: 112
  • 博客积分: 5245
  • 博客等级: 大校
  • 技术积分: 1120
  • 用 户 组: 普通用户
  • 注册时间: 2007-11-07 09:20
个人简介

静下来,定好方向,好好干。

文章分类
文章存档

2017年(1)

2012年(1)

2011年(5)

2010年(6)

2009年(16)

2008年(59)

2007年(24)

我的朋友

分类: LINUX

2007-11-15 17:18:25

驱动程序设计:
linux操作系统的驱动与bootloader的驱动区别:
要考虑与应用层的接口;
考虑多用户;
考虑其他协议;

设备驱动的作用:读数据,写数据;
初始化设备,读写设备;  将设备的数据分配给应用;  将应用的数据分配给设备;

操作系统中驱动和设备的关系是一一对应的;
应用和驱动的关系是一对多的;

内核的主要功能:进程管理,  内存管理,  文件系统,  设备控制,  网络;

linux驱动的分类:字符设备,  块设备,  网络设备;

大部分情况:主编号标识相应的驱动程序(现代linux允许多个驱动程序共享主编号), 次设备号标识哪个设备;

设备文件的cp:$ sudo cp -a /dev/mouse /tmp/  (此时是fopen打开设备文件)
             $ sudo cp    /dev/mouse /tmp/  (此时是open打开设备)

linux设备驱动信息的查看:
/proc/devices 查看系统支持的字符设备和块设备驱动;
/proc/pci 查看系统的PCI设备;
/proc/ioports 查看设备的IO端口;
/proc/interrupts 查看正在使用的中断号,中断次数;
/proc/net/dev 查看网络硬件设备,包括被down的网卡;
/proc/kallsyms 查看模块符号;
/proc/jitimer 查看定时器;
dmesg 查看系统的启动信息,可以看到系统支持的一些驱动的打印信息;
lspci
lsusb -v

uname -a
ifconfig -a  查看所有网卡

模块不全是驱动,但大多数模块都是驱动;

那些函数可以在模块中使用,关于kernel的API,可以查看

模块的重新编译:
在/usr/src/linux下解压linux源码包;
拷贝配置文件:cp /boot/config-2.6.20-16.generic /usr/src/linux/.config;
重新编译内核:sudo make
在模块的工程目录下编译模块:make

模块的参数传递:
module_param(name, type, perm);
static int count;
module_param(count,int,0);

#insmod driver.ko count=10
#lsmod
#rmmod driver

模块的交叉编译(arm开发板):
重新编译2.6.17.14内核,把zImage传给开发板;
将module工程目录拷贝到rootfs/usr/module下;
将module里的Makefile的源码路径变成2.6.17.14内核所在路径;
重新编译sudo make;

应用层--->TELNET,FTP,EMAIL
运输层--->TCP,UDP
网络层--->IP,ICMP,IGMP
链路层--->设备驱动程序及接口卡


USB设备驱动:
一些重要网站:
linux驱动的相关知识:下载Linux Device Drivers,2nd Edition
USB的相关资料:
              http://www.usbman.com/developer.htm
             
usb1.1规范:   http://www.usb.org/developer/docs/usbspec.zip
ohci协议:    
uhci协议:     http://developer.inter.com/technology/usb/UHCI11D.pdf

lsusb -v 查看USB设备信息;
Vendor:Product =1241:1111 这是USB设备的ID号需要向USB.ORG申请获得;
NumConfigurations = 1 表明配置描述符有1个;
bEndpointAddress = 81(in) 意思是1000 0001,最高位1是读, 后面1是端点号;
bmAttributes = 03(interrupt) 表明是中断类型的传送;因此有bInterval = 08(8ms)  表明间隔时间8ms;

工作过程:
USB主机控制器通过DMA方式访问所有设备;
对设备的读写是通过设备的端点来实现的;
USB主机通过协议链表来轮询这些端点,每个端点想要正常工作就要填充好端点格式,并放到协议链表中;
控制,实时,中断,大量传输需要不同的方式来填充协议链表;
当硬件读写完成通过中断方式来告诉CPU,并由CPU来处理剩下的工作;


字符设备C    块设备B    网络设备(无设备文件)
设备文件有文件节点, 但没有真正的内容;
主设备号标识与设备相连的驱动, 次设备号用来决定引用那个设备(一般如此, 非绝对);
设备号的内核类型dev_t
MAJOR( dev_t dev);   MINOR( dev_t dev);
MKDEV( int major, int minor);  
sudo cp -a /dev/input/mice   /tmp/   #fopen    拷贝文件
sudo cp /dev/input/mice   /tmp/   #open   打开设备
cat /proc/devices   pci   ioports   interrupts
lspci   //lsusb   -v    //cpuinfo   partitions   //dmesg   可查看模块输出信息
kallsyms   模块加载位置???
ln   -s   sda/c/   cc   建符号连接
sudo aptitude install   linux-source-2.6.20
模块的写法   头文件   参数的使用   延时参数的使用   用户空间与kernel空间的数据互传;
copy_to_user/copy_from_user         put_user/get_user

第一个linuxkernel模块实验:
uname -a   //查看当前版本
查看/user/src/  下是否有相应的linux source,    若无, 下载安装linux-source-2.6.20 
sudo   cp /boot/config-2.6.20-15.generic   /usr/src/linux-2.6.20/.config
sudo make
在module的编写目录下, make
再使用insmod   rmmod   lsmod
dmesg查看模块进出信息;

com2设置:  
ucon1<-0x5   轮询收发
ulcon1<-0x3   8N1  
ubrdiv1<-26   115200bps
状态寄存器:
UTRSTAT1,   bit[1 : 0]判断收发ready;
UTXH1->L(v) B(x)???发   URXH1->L(v) B(x)收;
对物理地址的读写, 有两种方法:
1.    #define UCON1 (*((volatile unsigned *)ioremap(0x500c4004, 4)))
2.   include
#define   iobase   S3C24XX_VA_UART1
#define   UCON1   (iobase + 0x04)
__raw_writel( 5, UCON1);
state = __raw_readb( UTRSTAT1);

\n换行\r回车   ~/.trash回收站   function(void)   void定义时不可少;
需包含一些 
实验主要流程:
将linux2.6.17.14代码解压到~/.目录下,   重新make menuconfig设置arm, 增加module support;
重新编译后, 下载内核zimage到2410板子上,
在driver编辑目录, 将makefile的src路径变为~/.linux2.6.17.14;
make, 获得driver. ko文件, 将他拷贝到rootfs/usr/driver/下
开发板用NFS方式启动, 在minicom中, 运行insmod driver.ko   ,lsmod   ,rmmod driver   观察结果;

SCULL设备驱动:
在编写设备驱动时遇到了以下问题:
1.   收发相同->buf[]指向了一个固定的字符串, 需要收发分开数组;
2.   发可以, 收不到->在驱动中收发用不同数组, 写可以动态申请, 收用数组是成功的???
3.   收发用数组指针如*buf++,   结果已经被移动, 用buf[tmp++]即可;
4.   在count经count--后, 却用在copy_to_user(buf, kbuf, cout)中, count已经为0了;
5.   recieve信息在终端显示被char release打断, 两种处理方式可行: close之前sleep(1);   release中不打印信息;
6.   tmp++后, return tmp正好是所读个数, 而不是tmp+1;

sudo -s   /su    username

字符设备驱动, GPIO驱动:
一些概念:
    dev_t为32位设备号(12+20);   scull_dev结构, cdev结构
    老注册方法:   register_chrdev(major, &name, &fops)
                            unregister_chrdev(major, &name)
    新注册方法:   regist_chrdev_region(dev_t, count, &name);
                            alloc_chrdev_region(&dev, firstminor, count, &name);
                            major = MAJOR(dev);
                            cdev_init(&cdev, &fops);   cdev. owner=THIS_MODULE;   cdev.ops=&fops;
                            cdev_add(&cdev, devno, 1);   cdev_del(&cdev);
                            unregister_chrdev_region( first, count);
关于中断:
    处理器管理设备的方式, 轮询, 中断+DMA;
    S3C2410支持56个中断设备, 32个中断号;   SRCPND, INTPND, INTMSK 写1清0;
    request_irq(irq, &handler, flags, &dev_name, &dev_id);         free_irq(irq, &dev_id);

GPIO实验:
注意这些目录:   asm/arch-s3c2410/regs-irq.h   asm/arc-s3c2410/regs-serial.h
寄存器名称:   S3C2410_EINTPEND
寄存器读写函数:   __raw_writel()   __raw_writeb()   __raw_readl()   __raw_readb()
寄存器SRCPND, INTPND, EINTPENT均写1清0;
irq_return_t   是中断服务函数的返回类型;
static struct semaphore key_sem;
up ( &key_sem);         down_interruptible ( &key_sem);
open时开中断, release时关中断, rmmod时free(irq);
set_irq_type   设置中断类型;

. bashrc: +PS1='$'   可改变shell提示符的显示内容;
推荐书籍:    linux内核设计与实现==陈莉君, linux驱动 倪继利 

网卡驱动:
网卡:   实模式, 286, 1M;/保护模式, 386, 4G
          BootRom: 启动novell网, IPX协议, 内存上虚拟一个盘;
          MAC协议层+PHY&传输层,物理层
MAC向IEEE申请6个16进制, 后6个16进制公司自配置;   00.xx.xx. yy.yy.yy
DEVICE ID标识厂家, 型号; 保存在设备列表中, 靠PCI, USB等协议维护;
网线, 4线, 8线有一组备用;
LCD, TVout, 若各自用单独振源, 将导致冲突;
网卡的调试方法:   硬件(电源, 晶振. . .);   软件(. . .);
将arm开发板的网卡去除后, 通过串口传程序的方法:
1.   启动pc端minicom, 启动开发板
2.   进入linux, cd /tmp
3.   rx文件名; C + A, S, 选择xmodem;
4.   选文件(双空格->进目录; 空格+回车);
PC端若不工作, 安装Lrzsz.
定义类型的几种情形:   typedef, struct, #define等; 查找方法:   grep str key$, key{, key空{   等方法;
S3C2410_IRQREG(x)   ->C(x) + S3C24XX_VA_IRQ)
Soket系统调用来操作网络设备, 而不是open一个设备文件;
看网卡硬件的方法, 确定有几个网卡;   cat /proc/net/dev         或ifconfig -a
看本机ip的一个方法, ping 一个无法联通的ip; (在没有ifconfig);
ifconfig up-->open         ifconfig down-->close;
register_netdev时会初始化net_device, 并调用init函数;
net_device的主要成员:   init, open(request_irq), stop(free_irq), hard_start_xmit, net_stats;
收发数据后, netif_rx把接收的数据交给协议层, netif-start/stop/wake-queue
#define   SMDK2410_ETH_IRQ   IRQEINT8
SMDK2410存在于四个目录:   include / asm ( arm-arm)/arch (arch-s3c2410);
双索引寄存器的操作, 可扩展存储空间;
lxr & source insight   两种方便用于源代码查看的工具;
网卡实验:
两个抓包工具:   tcpdump  -i   eth1   //   wire shark
主要步骤:
1.   启动无网络的linux;
2.   driver8900.ko, server, client下载到板子;
3.   mknod   /dev/mynet   c 242 0
4.   insmod   driver8900.ko
5.   . /server   or   . /client

硬件部分1015:
AGND-----L----DGND
存储模式, 带缓存         IO模式, 不带缓存;
Nandflash   D[7: 0 ]   数据/命令/地址复用
RAM用了6个管, SDRAM用了一个管子和一个电容;
candence兼并了orcad, 工具好用;

网卡实验:
1.   write(pp_txtmd), write( pp_txlength)   read(pp_busst);   若顺序反, 无法写成功;
2.   收到的ping包有64字节, 最后4字节有可能是CRC校验码;
3.   可以将buf={ 0x11, 0x12 . . .}放入头文件, 程序引用, 作为数据发送;
4.   open若用RDONLY会无法写成功;

可查看cat /proc/ioports/    观察IO空间;
EINT8->0XF0000000+36

USB1.1, USB2.0/主, 从, OTG设备(5芯)
USB缺点:   不稳定, 线<=5m;
北桥一般挂接高速设备, 如memory等;
PCI设备启动时要获取:   中断号, IO空间, memory空间;

启动ip, . /app -qws   (qt界面程序)
给2410上拷贝文件:   scp来实现;
sybian   赛班;   arm-elf-gcc v2.95.3

一般错误返回-1, 成功返回0;
Qimage可先生图形/setitimer在uclinux中定时;
"hello" + 1==>"ello";
if(defined(x))可以为变量;   ifdef   x   只能是宏;
2.0/3.0输出结果不同于2/3, 前者是float数据;
dpkg -L   nfs-kernel-server   显示安装目录
通常服务器配置问题可以通过拷贝正确的配置文件解决;

编程中用些宏定义, 条件编译, 注释说明等;


驱动的一些例程:

#driver makefile

ifneq ($(KERNELRELEASE),)
# kbuild part of makefile
obj-m :=driver.o
else

KERNELSRC:=/home/username/linux-2.6.17.14
modules:
    make -C $(KERNELSRC) SUBDIRS=$(PWD) $@

clean:
    rm -f *.o *.ko *.mod.c *~

endif

/*      driver.c
 * module example
 */

#include
#include
#include
#include
#include
#include

#include
#include
#include
#include
#include

#define iobase S3C24XX_VA_UART1

#define ULCON1 (iobase + 0x00)
#define UCON1 (iobase + 0x04)
#define UTRSTAT1 (iobase + 0x10)
#define UTXH1 (iobase + 0x20)
#define URXH1 (iobase + 0x24)
#define UBRDIV1 (iobase + 0x28)

MODULE_AUTHOR("xxxxxx");
MODULE_DESCRIPTION("com2 module");
MODULE_LICENSE("GPL");

int delay(int n)
{
    set_current_state(TASK_INTERRUPTIBLE);
    schedule_timeout(n);
    return 0;
}

int s3c2410_serial_read(void)
{
    char c = 'z';
    int state;

    printk("begin read...\n");
    while (c != 'Q')
    {
        state = __raw_readb(UTRSTAT1);
        if (state & 0X1)
        {
            c = __raw_readb(URXH1);
            printk("%c", c);
        }
        delay(1);
    }
    printk("\n");
    return 0;
}

int s3c2410_serial_write(void)
{
    int state;
    printk("begin write...\n");

    state = __raw_readb(UTRSTAT1);
    if (state & 0x2)
    {
        __raw_writel('*', UTXH1);
    }

    return 0;
}

int __init
s3c2410_serial_init(void)
{
    printk("comm2 init\n");
    __raw_writel(5, UCON1);
    __raw_writel(3, ULCON1);
    __raw_writel(26, UBRDIV1);

    s3c2410_serial_write();
    s3c2410_serial_read();
    return 0;
}
void __exit
s3c2410_serial_exit(void)
{
    printk("comm2 exit\n");
    return;
}

module_init(s3c2410_serial_init);
module_exit(s3c2410_serial_exit);


/*gpio_mod.c*/

#include
#include

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define keyirq_MAJOR 242

#define NOKEY '0'
#define KEY1  '1'
#define KEY2  '2'
#define KEY3  '3'
#define KEY4  '4'
#define KEY5  '5'


static struct semaphore key_sem;
static char key=NOKEY;

static void inline clearIrq(void)
{
    /*respond to EINT4-EINT7*/
    __raw_writel(0xf0, S3C2410_EINTPEND);
    __raw_writel(0x13, S3C2410_SRCPND);
    __raw_writel(0x13, S3C2410_INTPND);

}

static void key1_irq_isr(int irq, void *dev_id, struct pt_regs *regs)
{
    clearIrq();
    key = KEY1;
    up(&key_sem);

//    return 0;
}

static irqreturn_t key2_irq_isr(int irq, void *dev_id, struct pt_regs *regs)
{
    clearIrq();
    key = KEY2;
    up(&key_sem);

    return 0;
}

static irqreturn_t key3_irq_isr(int irq, void *dev_id, struct pt_regs *regs)
{
    clearIrq();
    key = KEY3;
    up(&key_sem);

    return 0;
}

static ssize_t keyirq_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
    down_interruptible(&key_sem);
    put_user(key, buf);
    key = NOKEY;

    return 1;
}

int keyirq_open(struct inode *inode, struct file *file)
{
    clearIrq();

#if 1
    enable_irq(IRQ_EINT0);
    enable_irq(IRQ_EINT1);
    enable_irq(IRQ_EINT6);
#endif

    sema_init(&key_sem, 0);
    return 0;
}

int keyirq_release(struct inode *inode, struct file *filp)
{
#if 1
    disable_irq(IRQ_EINT0);
    disable_irq(IRQ_EINT1);
    disable_irq(IRQ_EINT6);
#endif
    return 0;
}


static int keyirq_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
    return 0;
}

struct file_operations keyirq_fops = {
     .owner= THIS_MODULE,
     .read = keyirq_read,
     .open = keyirq_open,
     .release = keyirq_release,
     .ioctl = keyirq_ioctl,
};

int __init keyirq_init(void)
{
    int rc;

    printk("_SDK2410 gpio key driver irq=%d\n", IRQ_EINT0);

    set_irq_type(IRQ_EINT0, IRQT_FALLING);
    set_irq_type(IRQ_EINT1, IRQT_FALLING);
    set_irq_type(IRQ_EINT6, IRQT_FALLING);

    rc = request_irq(IRQ_EINT0, key1_irq_isr, SA_INTERRUPT, "key2345irq", NULL);
    if (rc) {
        printk ("<1>keyirq 1 irq not registered. Error:%d\n", rc);
    }

    rc = request_irq(IRQ_EINT1, key2_irq_isr, SA_INTERRUPT, "key2345irq", NULL);
    if (rc) {
        printk ("<1>keyirq 2 irq not registered. Error:%d\n", rc);
    }

    rc = request_irq(IRQ_EINT6, key3_irq_isr, SA_INTERRUPT, "key2345irq", NULL);
    if (rc) {
        printk ("<1>keyirq 4 irq not registered. Error:%d\n", rc);
    }

#if 1
    disable_irq(IRQ_EINT0);
    disable_irq(IRQ_EINT1);
    disable_irq(IRQ_EINT6);
#endif

    /*Register myirq as character device*/
    if ((rc = register_chrdev(keyirq_MAJOR, "key", &keyirq_fops)) < 0){
        printk("keyirq: can't get major %d\n", keyirq_MAJOR);
        return 1;
    }
    return 0;
}

void keyirq_exit(void)
{
#if 1
    disable_irq(IRQ_EINT0);
    disable_irq(IRQ_EINT1);
    disable_irq(IRQ_EINT6);
#endif

    free_irq(IRQ_EINT0, NULL);
    free_irq(IRQ_EINT1, NULL);
    free_irq(IRQ_EINT6, NULL);
   
    unregister_chrdev(keyirq_MAJOR, "key");
}

module_init(keyirq_init);
module_exit(keyirq_exit);

/*Discription: this program is used to test the driver gpio_mod.o
 *gpio_test.c
 */

#include
#include
#include
#include

#define NOKEY 0

int main()
{
    int keys4_fd;
    char ret[2];

    keys4_fd = open("/dev/keys4", O_RDONLY);
    if(keys4_fd <= 0)
    {
        printf("open keys4 device error!\n");
        return 0;
    }

    while(1)
    {
        read(keys4_fd, ret, 1);
        if(ret[0] != NOKEY)
        {
            printf("key = %c\n", ret[0]);
        }
        usleep(100000);
    }
    close(keys4_fd);
    return 0;
}

#driver makefile

ifneq ($(KERNELRELEASE),)
# kbuild part of makefile
obj-m :=gpio_mod.o
else

#KERNELSRC:=/usr/src/linux-headers-2.6.20-15-generic
KERNELSRC:=/home/username/linux-2.6.17.14

modules:
    make -C $(KERNELSRC) SUBDIRS=$(PWD) $@

clean:
    rm -f *.o *.ko *.mod.c *~

endif

test: gpio_test.c
    arm-linux-gcc -o gpio_test gpio_test.c

module: gpio_mod.c
    arm-linux-gcc -o gpio_mod gpio_mod.c


#driver makefile

ifneq ($(KERNELRELEASE),)
# kbuild part of makefile
obj-m :=char_dev.o
else

KERNELSRC:=/home/username/linux-2.6.17.14

modules:
    make -C $(KERNELSRC) SUBDIRS=$(PWD) $@

clean:
    rm -f *.o *.ko *.mod.c *~

endif

/*test_char.c*/

#include
#include
#include
#include
#include
#include

int main()
{
    int fd;
    int n;
    char buf[35] = "hellocc\n\r";


    fd = open("/dev/drvtst", O_RDWR);
    if (fd < 0)
    {
        perror ("open device err\n\r");
        exit(1);
    }
    write(fd, buf, strlen(buf));

    n = read(fd, buf, 30);
    buf[n] = '\0';
    printf ("%s\n", buf);
//    printf ("test over!\n");
    //sleep(1);
    close(fd); //call release();

    return 0;
}

/*char_dev.h -- definitions for the char module
 *
 * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
 * Copyright (C) 2001 O'Reilly & Associates
 *
 * The source code in this file can be freely used, adapted,
 * and redistributed in source or binary form, so long as an
 * acknowledgment appears in derived source files.  The citation
 * should list that the code comes from the book "Linux Device
 * Drivers" by Alessandro Rubini and Jonathan Corbet, published
 * by O'Reilly & Associates.   No warranty is attached;
 * we cannot take responsibility for errors or fitness for use.
 *
 * $Id: scull.h,v 1.15 2004/11/04 17:51:18 rubini Exp $
 */

#ifndef _SCULL_H_
#define _SCULL_H_

#include /* needed for the _IOW etc stuff used later */

/*
 * Macros to help debugging
 */

#undef PDEBUG             /* undef it, just in case */


#ifndef SCULL_MAJOR
#define SCULL_MAJOR 0   /* dynamic major by default */
#endif

#ifndef SCULL_NR_DEVS
#define SCULL_NR_DEVS 4    /* scull0 through scull3 */
#endif


/*
 * The bare device is a variable-length region of memory.
 * Use a linked list of indirect blocks.
 *
 * "scull_dev->data" points to an array of pointers, each
 * pointer refers to a memory area of SCULL_QUANTUM bytes.
 *
 * The array (quantum-set) is SCULL_QSET long.
 */
#ifndef SCULL_QUANTUM
#define SCULL_QUANTUM 4000
#endif

#ifndef SCULL_QSET
#define SCULL_QSET    1000
#endif

struct scull_dev {
    struct cdev cdev;      /* Char device structure        */
};
#endif /* _SCULL_H_ */

/*
 * char_dev.c -- the bare scull char module
 *
 * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
 * Copyright (C) 2001 O'Reilly & Associates
 *
 * The source code in this file can be freely used, adapted,
 * and redistributed in source or binary form, so long as an
 * acknowledgment appears in derived source files.  The citation
 * should list that the code comes from the book "Linux Device
 * Drivers" by Alessandro Rubini and Jonathan Corbet, published
 * by O'Reilly & Associates.   No warranty is attached;
 * we cannot take responsibility for errors or fitness for use.
 *
 */

#include
#include
#include

#include     /* printk() */
#include         /* kmalloc() */
#include         /* everything... */
#include     /* error codes */
#include     /* size_t */
#include
#include     /* O_ACCMODE */
#include
#include

#include
#include

#include         /* cli(), *_flags */
#include     /* copy_*_user */

#include
#include
#include
#include

#define SCULL_MAJOR 241
#include "char_dev.h"        /* local definitions */

///////////////////////////////////////////////////////////////////
#define UCON1 (*((volatile unsigned*)ioremap(0x50004004, 1)))
#define ULCON1 (*((volatile unsigned*)ioremap(0x50004000, 1)))
#define UBRDIV1 (*((volatile unsigned*)ioremap(0x50004028, 1)))
#define UTXH1 (*((volatile unsigned*)ioremap(0x50004020, 1)))
#define URXH1 (*((volatile unsigned*)ioremap(0x50004024, 1)))
#define UTRSTAT1 (*((volatile unsigned*)ioremap(0x50004010, 1)))
///////////////////////////////////////////////////////////////////
/*
 * Our parameters which can be set at load time.
 */


int scull_major =   SCULL_MAJOR;
int scull_minor =   0;
int scull_nr_devs = SCULL_NR_DEVS;    /* number of bare scull devices */
int scull_quantum = SCULL_QUANTUM;
int scull_qset =    SCULL_QSET;

module_param(scull_major, int, S_IRUGO);
module_param(scull_minor, int, S_IRUGO);
module_param(scull_nr_devs, int, S_IRUGO);
module_param(scull_quantum, int, S_IRUGO);
module_param(scull_qset, int, S_IRUGO);

//MODULE_AUTHOR("Alessandro Rubini, Jonathan Corbet");
//MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("user");
MODULE_DESCRIPTION("com2 module");
MODULE_LICENSE("GPL");

char *kbuf = 0;
char sbuf[100];

int delay(int n)
{
    set_current_state(TASK_INTERRUPTIBLE);
    schedule_timeout(n);
    return 0;
}
struct scull_dev *scull_devices;    /* allocated in scull_init_module */


/*
 * Open and close
 */

int scull_open(struct inode *inode, struct file *filp)
{
    printk ("char open\n");

    return 0;          /* success */
}

int scull_release(struct inode *inode, struct file *filp)
{
    printk ("char release \n");
    return 0;
}

/*
 * Data management: read and write
 */

ssize_t scull_read(struct file *filp, char __user *buf, size_t count,
                loff_t *f_pos)
{
    char c='z';
    int tmp = 0;
    printk ("read \n");
    //kbuf = kmalloc(count, 0);

    while ( count > 0)
    {
        delay(1);
        if (UTRSTAT1 & 0X1)
        {
            c = URXH1;
            sbuf[tmp++] = c;   
            count--;
        }
    }
    copy_to_user(buf, sbuf, tmp );
    //kfree(kbuf);

    printk("read over\n");
    return tmp;
}

ssize_t scull_write(struct file *filp, const char __user *buf, size_t count,
                    loff_t *f_pos)
{
    int tmp = 0;
    printk ("write \n");

    kbuf = kmalloc(count, 0);
    if (kbuf < 0)
    {
        printk(KERN_WARNING "scull: kmalloc failure %d\n", scull_major);
        return -1;
    }
    copy_from_user(kbuf, buf, count);

    while (count > 0)
    {
        if (UTRSTAT1 & 0x2)
        {
            UTXH1 = kbuf[tmp++];
            count--;
        }
    }
    kfree(kbuf);

    printk("write over\n");
    return 1;
}

/*
 * The ioctl() implementation
 */

int scull_ioctl(struct inode *inode, struct file *filp,
                unsigned int cmd, unsigned long arg)
{
    return 0;
}




struct file_operations scull_fops = {
    .owner =    THIS_MODULE,
    .read =     scull_read,
    .write =    scull_write,
    .ioctl =    scull_ioctl,
    .open =     scull_open,
    .release =  scull_release,
};

/*
 * Finally, the module stuff
 */

/*
 * The cleanup function is used to handle initialization failures as well.
 * Thefore, it must be careful to work correctly even if some of the items
 * have not been initialized
 */
void scull_cleanup_module(void)
{
    int i;
    dev_t devno = MKDEV(scull_major, scull_minor);

    /* Get rid of our char dev entries */
    if (scull_devices) {
        for (i = 0; i < scull_nr_devs; i++) {
            cdev_del(&scull_devices[i].cdev);
        }
        kfree(scull_devices);
    }


    /* cleanup_module is never called if registering failed */
    unregister_chrdev_region(devno, scull_nr_devs);

    printk ("cleanup_module char\n");
}


/*
 * Set up the char_dev structure for this device.
 */
static void scull_setup_cdev(struct scull_dev *dev, int index)
{
    int err, devno = MKDEV(scull_major, scull_minor + index);

    cdev_init(&dev->cdev, &scull_fops);
    dev->cdev.owner = THIS_MODULE;
    dev->cdev.ops = &scull_fops;
    err = cdev_add (&dev->cdev, devno, 1);
    /* Fail gracefully if need be */
    if (err)
        printk(KERN_NOTICE "Error %d adding scull%d", err, index);
}


int scull_init_module(void)
{
    int result, i;
    dev_t dev = 0;

    printk("comm2 init\n");
    UCON1 = 5;
    ULCON1 = 3;
    UBRDIV1 = 26;


    /*
     * Get a range of minor numbers to work with, asking for a dynamic
     * major unless directed otherwise at load time.
     */
    if (scull_major) {
        dev = MKDEV(scull_major, scull_minor);
        result = register_chrdev_region(dev, scull_nr_devs, "drvtst0");
    } else {
        result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,
                                     "drvtst1");
        scull_major = MAJOR(dev);
    }
    if (result < 0) {
        printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
        return result;
    }

        /*
     * allocate the devices -- we can't have them static, as the number
     * can be specified at load time
     */
    scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);
    if (!scull_devices) {
        result = -ENOMEM;
        goto fail;  /* Make this more graceful */
    }
    memset(scull_devices, 0, scull_nr_devs * sizeof(struct scull_dev));

        /* Initialize each device. */
    for (i = 0; i < scull_nr_devs; i++) {
        scull_setup_cdev(&scull_devices[i], i);
    }

    printk ("init ok\n");

    return 0; /* succeed */

  fail:
    scull_cleanup_module();
    return result;
}

module_init(scull_init_module);
module_exit(scull_cleanup_module);

#driver makefile

ifneq ($(KERNELRELEASE),)
# kbuild part of makefile
obj-m := driver8900.o
else

#KERNELSRC:=/usr/src/linux-headers-2.6.20-15-generic
KERNELSRC:=/home/username/linux-2.6.17.14

modules:
    make -C $(KERNELSRC) SUBDIRS=$(PWD) $@

clean:
    rm -f *.o *.ko *.mod.c *~

endif

server: server.c
    arm-linux-gcc -o $@ $^

client: client.c
    arm-linux-gcc -o $@ $^


/*server.c*/


#include
#include
#include
#include
#include
#include

int main()
{
    int fd, i, n;
    char buf[1024];


    fd = open("/dev/akaenet", O_RDWR);
    if (fd < 0)
    {
        perror ("open device err\n\r");
        exit(1);
    }

    while(1)
    {
        n = read(fd, buf, 1024);
        printf ("n = %d\n", n);
        for (i = 0; i < n; i++)
        {
            printf ("0x%02x, ", buf[i]);
            if ((i%8) == 7)
                printf("\n");
        }
        printf ("\nreceived: %s\n", buf);
    }

    close(fd);

    return 0;
}


/*ping_p.h*/

char ping_p[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xe0,
0x4d, 0x1f, 0x46, 0xb2, 0x08, 0x06, 0x00, 0x01,
0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x00, 0xe0,
0x4d, 0x1f, 0x46, 0xb2, 0xc0, 0xa8, 0x01, 0x7d,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8,
0x01, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x2d, 0x71, 0x35, 0xc8,
};

/*client.c*/

#include
#include
#include
#include
#include
#include
#include "ping_p.h"

int main()
{
    int fd;
    int n;
    //char buf[1024] = "666666666666666666666666666666666666666666666666666666";


    fd = open("/dev/akaenet", O_RDWR);
    if (fd < 0)
    {
        perror ("open device err\n\r");
        exit(1);
    }

    while(1)
    {
        //gets(buf);
        write(fd, ping_p, 60);
    //    write(fd, buf, strlen(buf));
    //    printf("ping_p[]...64\n");
        sleep(1);
    }

    close(fd); //call release();

    return 0;
}

/*cs8900.h*/

#ifndef CS8900_H
#define CS8900_H

/*
 * linux/drivers/net/cs8900.h
 *
 * Author: Abraham van der Merwe
 *
 * A Cirrus Logic CS8900A driver for Linux
 * based on the cs89x0 driver written by Russell Nelson,
 * Donald Becker, and others.
 *
 * This source code is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 */

/*
 * Ports
 */

#define PP_Address        0x0a    /* PacketPage Pointer Port (Section 4.10.10) */
#define PP_Data            0x0c    /* PacketPage Data Port (Section 4.10.10) */

/*
 * Registers
 */

#define PP_ProductID        0x0000    /* Section 4.3.1   Product Identification Code */
#define PP_MemBase            0x002c    /* Section 4.9.2   Memory Base Address Register */
#define PP_IntNum            0x0022    /* Section 3.2.3   Interrupt Number */
#define PP_EEPROMCommand    0x0040    /* Section 4.3.11  EEPROM Command */
#define PP_EEPROMData        0x0042    /* Section 4.3.12  EEPROM Data */
#define PP_RxCFG            0x0102    /* Section 4.4.6   Receiver Configuration */
#define PP_RxCTL            0x0104    /* Section 4.4.8   Receiver Control */
#define PP_TxCFG            0x0106    /* Section 4.4.9   Transmit Configuration */
#define PP_BufCFG            0x010a    /* Section 4.4.12  Buffer Configuration */
#define PP_LineCTL            0x0112    /* Section 4.4.16  Line Control */
#define PP_SelfCTL            0x0114    /* Section 4.4.18  Self Control */
#define PP_BusCTL            0x0116    /* Section 4.4.20  Bus Control */
#define PP_TestCTL            0x0118    /* Section 4.4.22  Test Control */
#define PP_ISQ                0x0120    /* Section 4.4.5   Interrupt Status Queue */
#define PP_TxEvent            0x0128    /* Section 4.4.10  Transmitter Event */
#define PP_BufEvent            0x012c    /* Section 4.4.13  Buffer Event */
#define PP_RxMISS            0x0130    /* Section 4.4.14  Receiver Miss Counter */
#define PP_TxCOL            0x0132    /* Section 4.4.15  Transmit Collision Counter */
#define PP_SelfST            0x0136    /* Section 4.4.19  Self Status */
#define PP_BusST            0x0138    /* Section 4.4.21  Bus Status */
#define PP_TxCMD            0x0144    /* Section 4.4.11  Transmit Command */
#define PP_TxLength            0x0146    /* Section 4.5.2   Transmit Length */
#define PP_IA                0x0158    /* Section 4.6.2   Individual Address (IEEE Address) */
#define PP_RxStatus            0x0400    /* Section 4.7.1   Receive Status */
#define PP_RxLength            0x0402    /* Section 4.7.1   Receive Length (in bytes) */
#define PP_RxFrame            0x0404    /* Section 4.7.2   Receive Frame Location */
#define PP_TxFrame            0x0a00    /* Section 4.7.2   Transmit Frame Location */

/*
 * Values
 */

/* PP_IntNum */
#define INTRQ0            0x0000
#define INTRQ1            0x0001
#define INTRQ2            0x0002
#define INTRQ3            0x0003

/* PP_ProductID */
#define EISA_REG_CODE    0x630e
#define REVISION(x)        (((x) & 0x1f00) >> 8)
#define VERSION(x)        ((x) & ~0x1f00)

#define CS8900A            0x0000
#define REV_B            7
#define REV_C            8
#define REV_D            9

/* PP_RxCFG */
#define Skip_1            0x0040
#define StreamE            0x0080
#define RxOKiE            0x0100
#define RxDMAonly        0x0200
#define AutoRxDMAE        0x0400
#define BufferCRC        0x0800
#define CRCerroriE        0x1000
#define RuntiE            0x2000
#define ExtradataiE        0x4000

/* PP_RxCTL */
#define IAHashA            0x0040
#define PromiscuousA    0x0080
#define RxOKA            0x0100
#define MulticastA        0x0200
#define IndividualA        0x0400
#define BroadcastA        0x0800
#define CRCerrorA        0x1000
#define RuntA            0x2000
#define ExtradataA        0x4000

/* PP_TxCFG */
#define Loss_of_CRSiE    0x0040
#define SQErroriE        0x0080
#define TxOKiE            0x0100
#define Out_of_windowiE    0x0200
#define JabberiE        0x0400
#define AnycolliE        0x0800
#define T16colliE        0x8000

/* PP_BufCFG */
#define SWint_X            0x0040
#define RxDMAiE            0x0080
#define Rdy4TxiE        0x0100
#define TxUnderruniE    0x0200
#define RxMissiE        0x0400
#define Rx128iE            0x0800
#define TxColOvfiE        0x1000
#define MissOvfloiE        0x2000
#define RxDestiE        0x8000

/* PP_LineCTL */
#define SerRxON            0x0040
#define SerTxON            0x0080
#define AUIonly            0x0100
#define AutoAUI_10BT    0x0200
#define ModBackoffE        0x0800
#define PolarityDis        0x1000
#define L2_partDefDis    0x2000
#define LoRxSquelch        0x4000

/* PP_SelfCTL */
#define RESET            0x0040
#define SWSuspend        0x0100
#define HWSleepE        0x0200
#define HWStandbyE        0x0400
#define HC0E            0x1000
#define HC1E            0x2000
#define HCB0            0x4000
#define HCB1            0x8000

/* PP_BusCTL */
#define ResetRxDMA        0x0040
#define DMAextend        0x0100
#define UseSA            0x0200
#define MemoryE            0x0400
#define DMABurst        0x0800
#define IOCHRDYE        0x1000
#define RxDMAsize        0x2000
#define EnableRQ        0x8000

/* PP_TestCTL */
#define DisableLT        0x0080
#define ENDECloop        0x0200
#define AUIloop            0x0400
#define DisableBackoff    0x0800
#define FDX                0x4000

/* PP_ISQ */
#define RegNum(x) ((x) & 0x3f)
#define RegContent(x) ((x) & ~0x3d)

#define RxEvent            0x0004
#define TxEvent            0x0008
#define BufEvent        0x000c
#define RxMISS            0x0010
#define TxCOL            0x0012

/* PP_RxStatus */
#define IAHash            0x0040
#define Dribblebits        0x0080
#define RxOK            0x0100
#define Hashed            0x0200
#define IndividualAdr    0x0400
#define Broadcast        0x0800
#define CRCerror        0x1000
#define Runt            0x2000
#define Extradata        0x4000

#define HashTableIndex(x) ((x) >> 0xa)

/* PP_TxCMD */
#define After5            0
#define After381        1
#define After1021        2
#define AfterAll        3
#define TxStart(x) ((x) << 6)

#define Force            0x0100
#define Onecoll            0x0200
#define InhibitCRC        0x1000
#define TxPadDis        0x2000

/* PP_BusST */
#define TxBidErr        0x0080
#define Rdy4TxNOW        0x0100

/* PP_TxEvent */
#define Loss_of_CRS        0x0040
#define SQEerror        0x0080
#define TxOK            0x0100
#define Out_of_window    0x0200
#define Jabber            0x0400
#define T16coll            0x8000

#define TX_collisions(x) (((x) >> 0xb) & ~0x8000)

/* PP_BufEvent */
#define SWint            0x0040
#define RxDMAFrame        0x0080
#define Rdy4Tx            0x0100
#define TxUnderrun        0x0200
#define RxMiss            0x0400
#define Rx128            0x0800
#define RxDest            0x8000

/* PP_RxMISS */
#define MissCount(x) ((x) >> 6)

/* PP_TxCOL */
#define ColCount(x) ((x) >> 6)

/* PP_SelfST */
#define T3VActive        0x0040
#define INITD            0x0080
#define SIBUSY            0x0100
#define EEPROMpresent    0x0200
#define EEPROMOK        0x0400
#define ELpresent        0x0800
#define EEsize            0x1000

/* PP_EEPROMCommand */
#define EEWriteEnable   0x00F0
#define EEWriteDisable  0x0000
#define EEWriteRegister    0x0100
#define EEReadRegister    0x0200
#define EEEraseRegister    0x0300
#define ELSEL            0x0400

#endif    /* #ifndef CS8900_H */

/*driver8900.c*/

#include
#include

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#include
#include
//#include

#include
#include

#include
#include
//#include
#include
#include
#include

// Added BSt
#include

#include "asm/arch/smdk2410.h"

#include "cs8900.h"

//#define FULL_DUPLEX
//#define DEBUG

#define netirq_MAJOR 242
#define NET_MAXLEN 1024
#define DEVICENAME "net8900"

static struct semaphore net_sem;
static u_char krbuf[NET_MAXLEN];
static u_char ktbuf[NET_MAXLEN];
static u32 cs8900_base_addr;
static int net_irq;
static int rxlen;

/********************************************************************************/
/*
 * I/O routines
 */

static inline u16 cs8900_read (u32 base_addr,u16 reg)
{
    outw (reg,base_addr + PP_Address);
    return (inw (base_addr + PP_Data));
}

static inline void cs8900_write (u32 base_addr,u16 reg,u16 value)
{
    outw (reg,base_addr + PP_Address);
    outw (value,base_addr + PP_Data);
}

static inline void cs8900_set (u32 base_addr,u16 reg,u16 value)
{
    cs8900_write (base_addr,reg,cs8900_read (base_addr,reg) | value);
}

static inline void cs8900_clear (u32 base_addr,u16 reg,u16 value)
{
    cs8900_write (base_addr,reg,cs8900_read (base_addr,reg) & ~value);
}   

static void cs8900_frame_read (u32 base_addr,u_char *buf,u16 length)
{
#if 1
    insw (base_addr,buf,(length + 1) / 2);
#else
    u16 tmp;
    int k;

    for (k = 0; k < length-1; k += 2)
    {
        //tmp = cs8900_read (cs8900_base_addr, PP_RxFrame);
        tmp = cs8900_read (cs8900_base_addr, PP_RxFrame + k);
        buf[k] = tmp & 0xff;
        buf[k+1] = tmp >> 8;
    }
    if (k == length-1)
    {
        tmp = cs8900_read (cs8900_base_addr, PP_RxFrame + k);
        buf[k++] = tmp & 0xff;
    }
#endif
}

static inline void cs8900_frame_write (u32 base_addr,u_char *buf, u16 length)
{
    outsw (base_addr,buf,(length + 1) / 2);
}
/********************************************************************************/
/*
 * Driver functionsdev
 */

/*
static void inline clearIrq(void)
{
    __raw_writel(0x100, S3C2410_EINTPEND);
    __raw_writel(0x20, S3C2410_SRCPND);
    __raw_writel(0x20, S3C2410_INTPND);
}
*/
static int cs8900_recv(void)
{
    u16 status,length;

    printk("in cs8900_recv ->0\n");
    status = cs8900_read (cs8900_base_addr,PP_RxStatus);
    length = cs8900_read (cs8900_base_addr,PP_RxLength);
    rxlen = length;

    if (!(status & RxOK)) {
        return 1;
    }

    printk("in cs8900_recv ->1\n");
    cs8900_frame_read (cs8900_base_addr,krbuf,length);
    up(&net_sem);
    printk("in cs8900_recv ->2\n");
    printk("recv length %d\n", length);
    return 0;
}

static int cs8900_send(int length)
{
    spinlock_t slock;
    u16 status;
   
    printk("in sending...\n");
    spin_lock_irq(&slock);
    cs8900_write (cs8900_base_addr,PP_TxCMD,TxStart (After5));
    cs8900_write (cs8900_base_addr,PP_TxLength, length);

    status = cs8900_read (cs8900_base_addr,PP_BusST);

    if ((status & TxBidErr)) {
        spin_unlock_irq(&slock);
        printk (KERN_WARNING "%s: Invalid frame size!\n",DEVICENAME);
        return (1);
    }

    if (!(status & Rdy4TxNOW)) {
        spin_unlock_irq(&slock);
        printk (KERN_WARNING "%s: Transmit buffer not free!\n",DEVICENAME);
        return (1);
    }

    cs8900_frame_write (cs8900_base_addr,ktbuf, length);
    spin_unlock_irq(&slock);
    printk("send length %d\n", length);
    printk("out sending...\n");
    return (0);
}

static irqreturn_t cs8900_irq_isr(int irq, void *dev_id, struct pt_regs *regs)
{
    volatile u16 status;
   irqreturn_t handled = 0;
 
    printk("into irq...\n");
    //clearIrq();
   
    while ((status = cs8900_read (cs8900_base_addr, PP_ISQ))) {
        handled = 1;
        switch (RegNum (status)) {
        case RxEvent:
            printk("receive Event\n");
            cs8900_recv();
            break;

        case TxEvent:
            printk("send Event\n");
            if (!(RegContent (status) & TxOK)) {
                printk("send error\n");
            }else{
                printk("send success\n");
            }
            break;

        case BufEvent:
            printk("Buf Event\n");
            break;

        case TxCOL:
            printk("TxCOL Event\n");
            break;

        case RxMISS:
            printk("RxMiss Event\n");
            break;

        default:
            printk ("interrupt not success!\n");
            break;
        }
        printk ("out switch...\n");
    }
    printk ("out while...\n");
    return IRQ_RETVAL(handled);   
}

static int cs8900_probe(void)
{
    int i,result;
    u16 value;
    spinlock_t slock;
    u_char dev_addr[ETH_ALEN];

    printk("into probe...\n");
    dev_addr[0] = 0x00;
    dev_addr[1] = 0x12;
    dev_addr[2] = 0x34;
    dev_addr[3] = 0x56;
    dev_addr[4] = 0x78;
    dev_addr[5] = 0x9a;
    //dev_addr[5] = 0x9b;

    spin_lock_init(&slock);

    cs8900_base_addr = vSMDK2410_ETH_IO + 0x300;
    net_irq = SMDK2410_ETH_IRQ;

    if ((result = check_mem_region (cs8900_base_addr, 16))) {
        printk (KERN_ERR "%s: can't get I/O port address 0x%lx\n", DEVICENAME, (unsigned long)cs8900_base_addr);
        return (result);
    }
    request_mem_region (cs8900_base_addr, 16, DEVICENAME);

    /* verify EISA regiscs8900_base_addrtration number for Cirrus Logic */
    if ((value = cs8900_read (cs8900_base_addr,PP_ProductID)) != EISA_REG_CODE) {
        printk (KERN_ERR "%s: incorrect signature 0x%.4x\n", DEVICENAME, value);
        return (-ENXIO);
    }

    /* verify chip version */
    value = cs8900_read (cs8900_base_addr,PP_ProductID + 2);
    if (VERSION (value) != CS8900A) {
        printk (KERN_ERR "%s: unknown chip version 0x%.8x\n", DEVICENAME, VERSION (value));
        return (-ENXIO);
    }

    /* setup interrupt number */
    cs8900_write (cs8900_base_addr,PP_IntNum,0);

    /*setup Mac address*/
    for (i = 0; i < ETH_ALEN; i += 2)
        cs8900_write (cs8900_base_addr,PP_IA + i,dev_addr[i] | (dev_addr[i + 1] << 8));


    spin_lock_init(&slock);

    printk ("Mac addr:");
    for (i = 0; i < ETH_ALEN; i += 2)
    {
        u16 mac = cs8900_read (cs8900_base_addr,PP_IA + i);
        printk ("%c%02X:%2X", (i==0)?' ':':', mac & 0xff, (mac >> 8));
    }
    printk ("\n");

    return (0);
}

static ssize_t scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
    down_interruptible(&net_sem);
    printk("ready to copy_to_user...\n");
    copy_to_user(buf, krbuf, rxlen);
    printk("end to copy_to_user...\n");
    printk("out read...\n");

    return rxlen;
}

static ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
    printk("into write...\n");
    copy_from_user(ktbuf, buf, count);
    cs8900_send(count);

    return 0;
}

static int scull_open(struct inode *inode, struct file *file)
{
    int result;

    //clearIrq();
    //disable_irq(net_irq);

    printk("into open...\n");
    set_irq_type(net_irq, IRQT_RISING);

    /* enable the ethernet controller */
    cs8900_set (cs8900_base_addr,PP_RxCFG,RxOKiE | BufferCRC | CRCerroriE | RuntiE | ExtradataiE);
    cs8900_set (cs8900_base_addr,PP_RxCTL,RxOKA | IndividualA | BroadcastA | PromiscuousA);
    cs8900_set (cs8900_base_addr,PP_TxCFG,TxOKiE | Out_of_windowiE | JabberiE);
    cs8900_set (cs8900_base_addr,PP_BufCFG,Rdy4TxiE | RxMissiE | TxUnderruniE | TxColOvfiE | MissOvfloiE);
    cs8900_set (cs8900_base_addr,PP_LineCTL,SerRxON | SerTxON);
    cs8900_set (cs8900_base_addr,PP_BusCTL,EnableRQ);

    cs8900_set (cs8900_base_addr,PP_RxCTL,MulticastA);
    //FULL_DUPLEX
    //cs8900_set (cs8900_base_addr,PP_TestCTL,FDX);

    sema_init(&net_sem, 0);
   
    udelay(1000);   

    /* install interrupt handler */
    if ((result = request_irq (net_irq, &cs8900_irq_isr, 0, DEVICENAME, NULL)) < 0) {
        printk (KERN_ERR "%s: could not register interrupt %d\n", DEVICENAME, net_irq);
        return (result);
    }

    //enable_irq(net_irq);

    return 0;
}

static int scull_release(struct inode *inode, struct file *filp)
{
    //disable_irq(net_irq);
   
    /* disable ethernet controller */
    cs8900_write (cs8900_base_addr,PP_BusCTL,0);
    cs8900_write (cs8900_base_addr,PP_TestCTL,0);
    cs8900_write (cs8900_base_addr,PP_SelfCTL,0);
    cs8900_write (cs8900_base_addr,PP_LineCTL,0);
    cs8900_write (cs8900_base_addr,PP_BufCFG,0);
    cs8900_write (cs8900_base_addr,PP_TxCFG,0);
    cs8900_write (cs8900_base_addr,PP_RxCTL,0);
    cs8900_write (cs8900_base_addr,PP_RxCFG,0);

    /* uninstall interrupt handler */
    free_irq (net_irq,NULL);

    return 0;
}


static int scull_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
    return 0;
}

struct file_operations scull_fops = {
    .owner= THIS_MODULE,
     .read = scull_read,
     .write = scull_write,
     .open = scull_open,
     .release = scull_release,
     .ioctl = scull_ioctl,
};

int __init scull_init(void)
{
    int rc;

    cs8900_probe();

    /*Register myirq as character device*/
    if ((rc = register_chrdev(netirq_MAJOR, DEVICENAME , &scull_fops)) < 0){
        printk("net driver: can't get major %d\n", netirq_MAJOR);
        return 1;
    }
    return 0;
}

void __exit scull_exit(void)
{
/*     release_region (cs8900_base_addr,16); */
    release_mem_region (cs8900_base_addr,16);

    unregister_chrdev(netirq_MAJOR, DEVICENAME);
}

MODULE_AUTHOR ("username");
MODULE_DESCRIPTION ("a scull&net device driver");
MODULE_LICENSE ("GPL");

module_init(scull_init);
module_exit(scull_exit);



阅读(2427) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~