Hello,
Below the driver source and header file.
to make it work:
1. compile it
2. enable PC104 bus
3. load driver
4. create filesystem node acording to major/minor number
5. build an user land application to send basic IOCTLs to the board(also check
board ID)
For sure the driver can be improved and I would be happy with any
sugestions/fixes for it.
In case of trouble I'll try to respond to your
I don't have any better ideea to post it, than listing below the files:
adc_drv.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <asm/uaccess.h>
//#include <asm/arch/regmap.h>
#include <linux/proc_fs.h>
#include <asm/io.h>
#include "/usr/src/linux/include/linux/ioport.h"
#include </usr/src/linux/include/linux/ioport.h>
#include <linux/interrupt.h>
#include "adc_drv.h"
MODULE_DESCRIPTION("ADC character dev");
MODULE_AUTHOR("Dan Lupu");
MODULE_LICENSE("GPL");
#define LOG_LEVEL KERN_DEBUG
struct my_adc_data {
struct cdev cdev;
/* my data starts here */
int size;
int* buffer; // buffer to store FIFO data
volatile char* ptr; /* board addr from k*/
unsigned int irq7Show;
};
irqreturn_t adc_irqHnd(int irq_no, void *dev_id)
{
struct my_adc_data *my_data = (struct my_adc_data *) dev_id;
printk("ADC IRQ Hnd called\n");
if(irq_no==0x07)
{
my_data->irq7Show = my_data->irq7Show + 1;
return IRQ_HANDLED;
}
return IRQ_NONE;
}
struct my_adc_data my_adc;
static int my_open(struct inode *inode, struct file *file);
static int my_read(struct file *file, char __user *user_buffer,
size_t size, loff_t *offset);
static int my_write (struct file *file, const char __user * user_buffer,
size_t size, loff_t *offset);
static int adc_ioctl (struct inode *inode,struct file *file,unsigned int cmd,
unsigned long arg);
static int my_release (struct inode *inode, struct file *file);
struct file_operations my_fops = {
.owner = THIS_MODULE,
.open = my_open,
.read = my_read,
.write = my_write,
.release = my_release,
.ioctl = adc_ioctl
};
static int my_init(void)
{
int err;
//printk(LOG_LEVEL "Init ADC module\n");
// offset adaugat isa_regbase.
my_adc.size=0;
my_adc.ptr = NULL;
my_adc.irq7Show=0;
err = register_chrdev_region(MKDEV(MY_MAJOR, MY_MINOR),1,MODULE_NAME);
if (err != 0) {
/* report error */
return err;
}
/* initialize my_dev fields */
cdev_init(&my_adc.cdev, &my_fops);
cdev_add(&my_adc.cdev, MKDEV(MY_MAJOR, MY_MINOR), 1);
//printk(LOG_LEVEL "Open ADC device\n");
if(check_mem_region(ADC_BASE,8))
{
printk("ADC_BASE space in use!");
return -EBUSY;
}
request_mem_region(ADC_BASE,0x1E,"adc_drv");
my_adc.ptr=__ioremap(ADC_BASE,0x1E,0);
if(my_adc.ptr == NULL)
{
printk("no IOREMAP success!\n");
}
// board ID
if(0x3e!=readb(my_adc.ptr))
{
printk("no ID from board 1");
if(0x3e!=inb(my_adc.ptr))
{
printk("no ID from board 2");
return -EBUSY;
}
}
// request IRQs
if ((err = request_irq(MY_IRQ1, adc_irqHnd, IRQF_SAMPLE_RANDOM |
IRQF_SHARED,
"adc", &my_adc))) {
/* handle error*/
printk("alocate IRQ7 not possible");
return err;
}
//allocate mem for ADC FIFO
my_adc.buffer=kmalloc(BUFFER_SIZE * sizeof(unsigned int),GFP_KERNEL);
return 0;
}
static void my_exit(void)
{
//printk(LOG_LEVEL "Exit\n");
release_mem_region(ADC_BASE,0x1E);
__iounmap(my_adc.ptr);
free_irq(MY_IRQ1, &my_adc);
cdev_del(&my_adc.cdev);
kfree(my_adc.buffer);
unregister_chrdev_region(MKDEV(MY_MAJOR, MY_MINOR), 1);
}
static int my_open(struct inode *inode, struct file *file)
{
struct my_adc_data *my_adc;
//printk(LOG_LEVEL "Open DIO device\n");
my_adc = container_of(inode->i_cdev, struct my_adc_data, cdev);
file->private_data = my_adc;
my_adc->size=0;
// writeb(0x00,my_adc->ptr+DDR);
return 0;
}
static int my_read(struct file *file, char __user *user_buffer,
size_t size, loff_t *offset)
{
struct my_adc_data *my_data =
(struct my_adc_data*) file->private_data;
size_t toberead;
unsigned int ind;
if(size >= BUFFER_SIZE)
return 0;
if(*offset >= BUFFER_SIZE)
return 0;
//printk(LOG_LEVEL "[my_read] Trying to read %d bytes\n", size);
// first read in the buffer
for(ind=0;ind<size;ind++){
my_data->buffer[ind]=readw(my_data->ptr+ADCFIFO);
}
// toberead = (size < my_data->size - *offset) ? size : my_data->size -
*offset;
if(copy_to_user(user_buffer, my_data->buffer, size))
return -EFAULT;
return size;
}
static int my_write (struct file *file, const char __user * user_buffer,
size_t size, loff_t *offset)
{
// struct my_adc_data *my_adc = (struct my_adc_data*) file->private_data;
size_t tobewritten;
return tobewritten;
}
static int adc_ioctl (struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
unsigned int adccfg,adcstat,count,temp;
unsigned int retval=0;
struct my_adc_data *my_adc = (struct my_adc_data*) file->private_data;
switch(cmd) {
case ADC_BID_IOCTL:
retval=ioread16(my_adc->ptr);
break;
case ADC_CFG_IOCTL:
//printk(LOG_LEVEL "set ADCCFG %d\n",arg);
iowrite16(0x167, my_adc->ptr+ADCCFG);
//writeb(0x67, my_adc->ptr+ADCCFG+1);
break;
case ADC_GETCFG_IOCTL:
//get by pointer.
// cal this from user space
ioctl(fd,ADC_GETCFG_IOCTL,&adccfg);uint adccfg;
adccfg = ioread16( my_adc->ptr+ADCCFG);
retval = put_user(adccfg, (int __user *)arg);
//printk(LOG_LEVEL "get ADCCFG W %d\n",adccfg);
//printk(LOG_LEVEL "get ADCCFG MSB %d\n",ioread8( my_adc->ptr +
ADCCFG ));
//printk(LOG_LEVEL "get ADCCFG LSB %d\n",ioread8( my_adc->ptr +
ADCCFG+1 ));
break;
case ADC_DLYLSW_IOCTL:
iowrite16(arg, my_adc->ptr + ADC_DLY_LSW);
break;
case ADC_DLYLSWR_IOCTL:
temp =ioread16(my_adc->ptr + ADC_DLY_LSW);
retval = __put_user(temp, (int __user *)arg);
break;
case ADC_DLYMSW_IOCTL:
writew(arg, my_adc->ptr + ADC_DLY_MSW);
break;
case ADC_STAT_IOCTL:
writew(arg, my_adc->ptr + ADCSTAT);
break;
case ADC_GETSTAT_IOCTL:
adcstat=readw(my_adc->ptr + ADCSTAT);
retval = __put_user(adcstat, (int __user *)arg);
break;
case ADC_DACCFG_IOCTL:
//printk(LOG_LEVEL "[my_ioctl] [MY_IOCTL_PRINT] Hello ioctl
DDRB SET\n");
writew(arg, my_adc->ptr + DACCFG);
break;
case ADC_DACCMDR_IOCTL:
//printk(LOG_LEVEL "[my_ioctl] [MY_IOCTL_PRINT] Hello ioctl
DDRB SET\n");
retval=readw(my_adc->ptr + DACCMD);
break;
case ADC_DACCMDRP_IOCTL:
//printk(LOG_LEVEL "[my_ioctl] [MY_IOCTL_PRINT] Hello ioctl
DDRB SET\n");
temp=readw(my_adc->ptr + DACCMD);
retval=__put_user(temp, (int __user *)arg);
break;
case ADC_DACCMD_IOCTL:
//printk(LOG_LEVEL "[my_ioctl] [MY_IOCTL_PRINT] Hello ioctl
DDRB SET\n");
writew(arg, my_adc->ptr + DACCMD);
break;
case ADC_DACSTART_IOCTL:
writeb((unsigned char)arg, my_adc->ptr + DACCMD+1);
break;
// count 0-3
case ADC_COUNT0_IOCTL:
count=readw(my_adc->ptr + COUNT0);
retval = __put_user(count, (int __user *)arg);
break;
case ADC_COUNT1_IOCTL:
count=readw(my_adc->ptr + COUNT1);
retval = __put_user(count, (int __user *)arg);
break;
case ADC_COUNT2_IOCTL:
count=readw( my_adc->ptr + COUNT2);
retval = __put_user(count, (int __user *)arg);
break;
case ADC_COUNT3_IOCTL:
count=readw( my_adc->ptr + COUNT3);
retval = __put_user(count, (int __user *)arg);
break;
case ADC_DIGIO_IOCTL:
writew(arg, my_adc->ptr + DIGIO);
break;
}//switch
return retval;
}
static int my_release (struct inode *inode, struct file *file)
{
//printk(LOG_LEVEL "[my_release] Device released.\n");
return 0;
}
module_init(my_init);
module_exit(my_exit);
adc_drv.h
#define MY_MAJOR 43
#define MY_MINOR 0
#define MODULE_NAME "adc"
#define ISAREGSBASE (0xE8000000)
#define ISABASE (0xEE000000)
#define IOPOFFSET 0x120 // JP1-ON JP2-OFF
#define ADCCFG 0x02
#define ADC_DLY_MSW 0x04
#define ADC_DLY_LSW 0x06
#define ADCSTAT 0x08
#define ADCFIFO 0x0A
#define DACCFG 0x0C
#define DACCMD 0x0E
#define COUNT0 0x10
#define COUNT1 0x12
#define COUNT2 0x14
#define COUNT3 0x16
#define DIGIO 0x18
#define EXTRIG_BIT 0x09
#define SEDIF_BIT 0x08
// isa base + 0x120. JP1=on,JP2=off
#define ADC_BASE (0xEE000120)
#define BUFFER_SIZE 512
//#define BOARD_ID ((volatile char*)(my_data->ptr))
//#define RO1 ((volatile char*)(my_data->ptr+1))
#define BOARD_ID 0
#define ADC_DRV_IOC_MAGIC 141
#define ADC_CFG_IOCTL _IOWR (ADC_DRV_IOC_MAGIC, 1, int)
#define ADC_DLYMSW_IOCTL _IOW (ADC_DRV_IOC_MAGIC, 2, int)
#define ADC_DLYLSW_IOCTL _IOW (ADC_DRV_IOC_MAGIC, 3, int)
#define ADC_STAT_IOCTL _IOWR (ADC_DRV_IOC_MAGIC, 4, int)
#define ADC_FIFO_IOCTL _IOR (ADC_DRV_IOC_MAGIC, 5, int)
#define ADC_DACCFG_IOCTL _IOR (ADC_DRV_IOC_MAGIC, 6, int)
#define ADC_DACCMD_IOCTL _IOW (ADC_DRV_IOC_MAGIC, 7, int)
#define ADC_COUNT0_IOCTL _IOR (ADC_DRV_IOC_MAGIC, 8, int)
#define ADC_COUNT1_IOCTL _IOR (ADC_DRV_IOC_MAGIC, 9, int)
#define ADC_COUNT2_IOCTL _IOR (ADC_DRV_IOC_MAGIC, 10, int)
#define ADC_COUNT3_IOCTL _IOR (ADC_DRV_IOC_MAGIC, 11, int)
#define ADC_DIGIO_IOCTL _IOWR (ADC_DRV_IOC_MAGIC, 12, int)
#define ADC_BOARD_IOCTL _IOR (ADC_DRV_IOC_MAGIC, 13, int)
#define ADC_GETCFG_IOCTL _IOR (ADC_DRV_IOC_MAGIC, 14, int)
#define ADC_GETSTAT_IOCTL _IOR (ADC_DRV_IOC_MAGIC, 15, int)
#define ADC_BID_IOCTL _IOR (ADC_DRV_IOC_MAGIC, 16, int)
#define ADC_DACCMDR_IOCTL _IOR (ADC_DRV_IOC_MAGIC, 17, int)
#define ADC_DACCMDRP_IOCTL _IOR (ADC_DRV_IOC_MAGIC, 18, int)
#define ADC_DLYLSWR_IOCTL _IOR (ADC_DRV_IOC_MAGIC, 19, int)
#define ADC_DACSTART_IOCTL _IOW (ADC_DRV_IOC_MAGIC, 20, unsigned char)
#define MY_IRQ1 7
------------------------------------
Yahoo! Groups Links
<*> To visit your group on the web, go to:
http://groups.yahoo.com/group/ts-7000/
<*> Your email settings:
Individual Email | Traditional
<*> To change settings online go to:
http://groups.yahoo.com/group/ts-7000/join
(Yahoo! ID required)
<*> To change settings via email:
<*> To unsubscribe from this group, send an email to:
<*> Your use of Yahoo! Groups is subject to:
http://docs.yahoo.com/info/terms/
|