⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 spi.c

📁 SPI是一种新代的串口驱动。这是linux下的
💻 C
字号:
/* SPI driver   Version 2.02 (Bug fixed in cleanup_module) 29.Oct.2001*/#ifndef __KERNEL__#  define __KERNEL__#endif#ifndef MODULE#  define MODULE#endif#ifdef MODULE#include <linux/module.h>#include <linux/version.h>/* */#else#include <linux/init.h>#define MOD_INC_USE_COUNT#define MOD_DEC_USE_COUNT#endif#ifdef CONFIG_MODVERSIONS#ifndef MODVERSIONS#define MODVERSIONS#endif#include <linux/modversions.h>#include <linux/config.h>#endif/* #include <linux/byteorder/generic.h>*//*#include <linux/errno.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/major.h>#include <linux/sched.h>*/#include <linux/malloc.h>#include <linux/ioport.h>#include <linux/fcntl.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/segment.h>#include <asm/system.h>#include "spi.h"#define SPI_BASE base   // base = 0x378#define SPI_DATA base#define SPI_STAT base+1#define SPI_CNTL base+2#define SPI_IDLE 3		/* idle state of MOSI and SCK is low */#define SPI_NOCE idle_ce	#define SPI_CLK 2		/* bit 1 of command reg (AUTO_LF) */#define SPI_DIN_LINE 0x80	/* bit 7 of status reg (BUSY) */static	int (* spi_bit_driver) (int);static	int	openflag;  // this flag is very important!!static  unsigned char spi_ctl_bck;static  int base = 0x378;	/* port ot use per default */static  int idle_ce = 0xff;	/* set all CEs high per default */unsigned int spi_major = 60; 	/* a local major, can be overwritten */static unsigned char * spi_buf = NULL;	/* pointer to the buffer in use */static int spi_buf_len = 0;	/* len of allocated buffer */	#ifdef MODULEMODULE_PARM(base, "1i");	/* accepts 1 integer as parameter */MODULE_PARM(idle_ce, "1i");	/* accepts 1 integer as parameter */MODULE_PARM(spi_major, "1i");	/* accepts 1 integer as parameter */#endif/* Some support routines first */static int spi_transfer_byte (int databyte)/* spi_transfer_byte sends one byte over the SPI bus and concurrently   receives one (as specified in the SPI specs). The MSB is    transferred first.   It expects the byte in the low byte of the int and returns the   result in the low byte. The reason, that ints are used is to   simplify the code.*/{  int i;        /* the counter */  int inval=0;  /* temp. var for the byte to read in */	  int outval;   /* same for byte to send */  for (i=0;i<8;i++)  { 			/* loop 8 times */    inval = inval << 1;                 /* shift new bit up */    databyte = databyte << 1;           /* shift MSB of lower byte into HB */    outval = (databyte & 0x100) >> 8;   /* mask bit, shift to LSB */    inval=inval | (spi_bit_driver(outval)==0 ? 1 : 0 );	 /* transfer bit in MSB */    }/*end-for*/                          /* one byte is transferred */  return inval ;	                   }	static int pha0_driver (int bit)/* this is the driver for any microwire device.It sends a one if "bit" is not zero and returns a non-zero valueif it read in a one. The bit set in the return value is not defined! If a zerovalue is returned, a zero was read in and if a non-zero value is returned a onewas read in. */{  int inxx ;	/* we must keep the bit for a while */  outb_p(spi_ctl_bck ^ bit, SPI_CNTL); /* put out bit, deactivate SCK */  inxx = inb_p(SPI_STAT) & SPI_DIN_LINE; /* read bit before clock pulse */   outb_p( (spi_ctl_bck ^ bit) ^ SPI_CLK, SPI_CNTL );  /* activate SCK */  return inxx;  }static int pha1_driver (int bit)/* this is the driver for any SPI device.It sends a one if "bit" is not zero and returns a non-zero valueif it read in a one. The bit set in the return value is not defined! If a zerovalue is returned, a zero was read in and if a non-zero value is returned a onewas read in. */{  int tmpo;  tmpo = spi_ctl_bck ^ bit;		/* we need it more than once */  outb_p(tmpo, SPI_CNTL);   		/* out bit, deactivate SCK */  outb_p(tmpo ^ SPI_CLK, SPI_CNTL);   	/* out bit, activate SCK */  outb_p(tmpo, SPI_CNTL);        	/* put out bit, deactivate SCK */  outb_p(tmpo, SPI_CNTL);        	/* again, to let line settle */  return inb_p(SPI_STAT) & SPI_DIN_LINE;/* read bit after clock pulse */}int select_proto (int proto){  int retval = 0;  switch(proto)   {    case SEL_SPI_CPOL_1:      spi_bit_driver = pha1_driver;           /* set the correct bit driver */      spi_ctl_bck = spi_ctl_bck & ~SPI_CLK ;  /* set clock high */      outb_p(spi_ctl_bck, SPI_CNTL);		      break;    case SEL_SPI_CPOL_0:      spi_bit_driver = pha1_driver;           /* set the correct bit driver */      spi_ctl_bck = spi_ctl_bck | SPI_CLK ;   /* set clock low */      outb_p(spi_ctl_bck, SPI_CNTL);		      break;    case SEL_UWIRE_CPOL_0:      spi_bit_driver = pha0_driver;          /* set the correct bit driver */      spi_ctl_bck = spi_ctl_bck | SPI_CLK ;  /* set clock low */      outb_p(spi_ctl_bck, SPI_CNTL);		      break;    case SEL_UWIRE_CPOL_1:      spi_bit_driver = pha0_driver;          /* set the correct bit driver */      spi_ctl_bck = spi_ctl_bck & ~SPI_CLK ; /* set clock high */      outb_p(spi_ctl_bck, SPI_CNTL);		      break;    default:      retval = -EINVAL;    }    return retval;}	/* here come the driver functions now */	static int spi_reset(void){  outb_p(SPI_NOCE, SPI_DATA);	/* set all CEs incative */  outb_p(SPI_IDLE, SPI_CNTL);  spi_ctl_bck = SPI_IDLE ;	/* remember the default idle state */  return (inb_p(SPI_STAT));	/* return status, just for fun */}static int spi_write(struct file * file, const char * buf, 		size_t count, loff_t *ppos)/* write data in buffer to spi bus and concurrently read in new.The buffer must have the following structure in it: (1 byte per entry)___________________________________________| protocol specifier in the least low bits | |------------------------------------------||     CE settings to use at the start      ||------------------------------------------||             data byte 1                  ||------------------------------------------||             data byte 2                  ||------------------------------------------||                 ...                      ||------------------------------------------||            last data byte                ||------------------------------------------||  CE settings, that are to left behind    |--------------------------------------------	   As there must be at least one data byte the minimum count is 4(3 bytes overhead and at least one data byte)The starting CE settings are applied just before the first byteis transferred. The ending CE settings are applied after the lastdata byte is transferred. In a one byte message the startingsetting adresses a chip and the ending deadresses it. When talkingto chips that need multibyte transfers with a pause inbetween, however, the start settings of the whole action adresses the chipas usual, but the ending settings of any intermediate transferleaves the chip adressed.	   Each byte received supersedes the corresponding byte sent in thebuffer. Thus the buffer contains the received data bytes onreturn from this function. As there are always exactly as manybytes received as sent, there cannot be an overflow of the buffer.*/	   {  int tmp;  int i;  unsigned char * bufptr;  if(count<4) return -EINVAL;          	/* must be at least 4 bytes in buf*/  if (spi_buf != NULL)   {		/* buffer already allocated? */     if (spi_buf_len < count)      {		/* yes, new write bigger than last? */        kfree(spi_buf);			/* yes, free the old buffer */        spi_buf_len = count;			/* save requested len for the read */        spi_buf = kmalloc(count, GFP_KERNEL);	/* allocate new buffer */     }					/* old buffer is big enough for write */  }   else   {				/* no buffer allocated */     spi_buf_len = count;			/* save requested len for the read */     spi_buf = kmalloc(count, GFP_KERNEL);	/* allocate buffer */  }  copy_from_user(spi_buf, buf, count);	/* copy data from user space */  bufptr = spi_buf;			/* point working pointer to our buf */  tmp = select_proto(*bufptr++);     	/* set the protocol */  if (tmp == -EINVAL)   {			/* wrong protocol specified */     kfree(spi_buf);			/* free buf */     spi_buf = NULL;			/* invalidate pointer to buf */     spi_buf_len = 0;			/* set len of buf to zero */     return -EINVAL ;  }    outb_p(*bufptr++, SPI_DATA); 		/* set the CEs */  for (i=count-3; i>0; i--)   {    tmp=spi_transfer_byte(*bufptr);	/* transfer a byte over the bus */    *bufptr++ = tmp;;           	/* write returned  byte to buffer */  }/*end-for*/                         	/* all data bytes transferred now */    outb_p(spi_ctl_bck, SPI_CNTL); 	/* deactivate SCK */  outb_p(*bufptr, SPI_DATA); 		/* the ending CE settings */  return count;                        	/* we can always send all data */}static int spi_read(struct file * file, 			char * buf, size_t count, loff_t *ppos){   int len;  if (spi_buf == NULL) return 0;	/* no bytes read when buf invalid */  if (count > spi_buf_len) len = spi_buf_len;  else len = count;			/* prevent reading beyond buf */  copy_to_user(buf, spi_buf, len);	/* send the complete buf to user */  kfree(spi_buf);			/* dealloc buffer */  spi_buf = NULL;			/* invalidate pointer to buffer */  spi_buf_len = 0;			/* set len of buf to zero */  return len;				}static int spi_open(struct inode * inode, struct file * file){  if (openflag) return -EBUSY;         /* only one access at a time */  openflag=1;                          /* mark it already opened */  return 0;}static int spi_release(struct inode * inode, struct file * file){  openflag=0;  return 0;}/* static struct file_operations spi_fops  */static struct file_operations spi_fops = { // here is the functions to be called   NULL,  spi_read,        spi_write,  NULL,          /* spi_readdir */  NULL,          /* spi_select */  NULL,		 /* ioctl */	  NULL,          /* spi_mmap */  spi_open,   NULL,  spi_release,   };void cleanup_module(void){    if (spi_major > 0) unregister_chrdev(spi_major, "spi");  // this function is where ?    release_region(SPI_BASE,3);    if (spi_buf != NULL) kfree (spi_buf);}int init_module(void){  int result = register_chrdev(spi_major,"spi",&spi_fops);  // this function is where ?  if (result < 0) {    printk("SPI: unable to get major %d for SPI bus \n", spi_major);    return result;  }/*end-if*/    if (spi_major == 0) spi_major = result; /* here we got our major dynamically */  /* reserve our port, but check first if free */  if (check_region(SPI_BASE, 3)) {    // oh!my god !a kernel function!    printk("SPI: port at adr 0x%04x already occupied\n", SPI_BASE);    unregister_chrdev(spi_major, "spi");   // Cancel the registeration    return result;  }/*end-if*/  /* write to port & read back to check */  outb_p(0, SPI_DATA);  if (inb_p(SPI_DATA) == 0)   {    spi_ctl_bck = SPI_IDLE ;    spi_reset();    request_region(SPI_BASE, 3, "spi");    printk("SPI bus at 0x%04x \n", SPI_BASE);    return 0;  }   else   {    cleanup_module();    printk("SPI: no chips at 0x%04x \n", SPI_BASE);    return -EINVAL ;  }/*end-if*/}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -