SC16IS752驱动编写与调试记录

折腾了几天,终于把spi扩展串口给搞定了,这个芯片有两个通道、可配置波特率、支持IIC和spi通信,支持中断等诸多优良特性。
主cpu是采用AT91SAM9X35平台,linux内核采用的是2.6.39,文件系统是用buildroot生成的。
由于之前用通用驱动来操作752芯片,所以,我们需要先关闭内核通用驱动的配置(不关闭也是可以的,modalias一改,通用驱动就匹配不上了)。好了,先上原理图:
  SC16IS752驱动编写与调试记录_第1张图片
通过查看原理图,可以知道采用的是SPI模式,并且与cpu的spi0相连,IRQ中断和cpu的PB18引脚。
打开board-sam9x5ek.c文件,先把原来通用驱动的板级配置注释掉:
/*
static struct spi_board_info at91sam9x5_spi_devices[] = {
        {
                .modalias= "spidev",
		     
                .max_speed_hz= 1000000,
									
                .bus_num= 0,  
                .chip_select= 0,
		.mode	= SPI_MODE_0,
											   
	},
};
*/
然后加上如下的代码:
static struct spi_board_info at91sam9x5_spi_devices[] = {


        {
                .modalias= "sc16is752",
		     
                .max_speed_hz= 1000000,
									
                .bus_num= 0,
                .chip_select= 0,
		   .mode	= SPI_MODE_0,
		   .irq = AT91_PIN_PB18,
											   
	},
};

.modalias是设置spi设备名字的,我们设置为“sc16is752”.max_speed_hz是设置spi时钟频率,752最大支持4Mhz。.bus_num是表示采用哪一个控制器的,假如我们采用spi1,则bus_num设置为1,chip_select表示的是高电平还是低电平选择752。Mode表示是使用哪种模式,spi有四种模式,关于四种模式,百度有详细介绍,通过查看752芯片手册,我们知道752工作在模式0。Irq是表示中断号,752irq是和PB18相连接的,所以设置为AT91_PIN_PB18,AT91_PIN_PB18是一个宏,展开后是82,有兴趣的自己去了解smx35的硬件中断。需要注意的是,这不是spi的中断,别搞错了,是752外设FIFO缓冲区有内容的中断。
 
板级配置好了之后,会通过ek_board_init函数,然后调用at91_add_device_spi(at91sam9x5_spi_devices, ARRAY_SIZE(at91sam9x5_spi_devices)); 将板级文件信息注册到linux内核。
接下来编写驱动,新建文件:sc16is752.c:
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


#define     RHR			0x00			//R
#define     THR			0x00			//W


#define     IER			0x01


#define     FCR			0x02			//R
#define     IIR			0x02			//W


#define     LCR			0x03
#define     MCR			0x04
#define     LSR			0x05
#define     MSR_1		0x06			//本来是直接定义为MSR的,但是这样定义与汇编指令MSR有冲突
#define     SPR			0x07


#define     TCR			0x06			//These registers are accessible only when EFR[4] = 1, and MCR[2] = 1
#define     TLR			0x07


#define     TXLVL		0x08
#define     RXLVL		0x09
#define     IODIR_752		0x0a
#define     IOSTATE		0x0b
#define     IOINTENA		0x0c
#define     RESERVED		0x0d
#define     IOCONTROL		0x0e
#define     EFCR		0x0f




//special register The Special Register set is accessible only when LCR[7] = 1 and not 0xBF
#define     DLL			0x00
#define     DLH			0x01




//enhanced register Enhanced Features Registers are only accessible when LCR = 0xBF
#define     EFR			0x02
#define     XON1		0x04
#define     XON2		0x05
#define     XOFF1		0x06
#define     XOFF2		0x07


//定义端口
#define	channelA		0x0
#define	channelB		0x1


//定义spi uart操作幻数
#define SET_channleA _IOW('k', 0, int)
#define SET_channleB _IOW('k', 1, int)
#define SET_STOP_BIT _IOW('k', 2, int)
#define SET_BPS _IOW('k', 3, int)


struct sc16is752_dev {
	struct spi_device *spi;
	spinlock_t spi_lock;
	struct mutex lock;
	unsigned char *buf;
	unsigned int channel_num;
	int irq;
	struct work_struct *sc752_irq_work;
	wait_queue_head_t sc16is752_q;
	unsigned int condition;
};


static struct sc16is752_dev *sc16is752 = NULL; 


static char read_752_reg(unsigned int reg, unsigned int channel)
{
	struct spi_transfer t[2];
	struct spi_message m;
	char val = 0;
	
	unsigned char cmd = 0x80 | (reg << 3) | (channel << 1);
	
	spi_message_init(&m);
	memset(t, 0, (sizeof t));
	
	t[0].tx_buf = &cmd;
	t[0].len = 1;
	spi_message_add_tail(&t[0], &m);
	
	t[1].rx_buf = &val;
	t[1].len = 1;
	spi_message_add_tail(&t[1], &m);
	
	spin_lock_irq(&sc16is752->spi_lock);
	
	spi_sync(sc16is752->spi, &m);


	spin_unlock_irq(&sc16is752->spi_lock);
	
	return val;	
}


static ssize_t read_channel(int len)
{
#if 0
	struct spi_transfer t[2];
	struct spi_message m;
	unsigned char cmd;
	
	cmd = 0x80 | (THR << 3) | (sc16is752->channel_num<< 1);
	printk(KERN_INFO "enter read_channel\n");
	spi_message_init(&m);
	memset(t, 0, (sizeof t));
	
	t[0].tx_buf = &cmd;
	t[0].len = 1;
	spi_message_add_tail(&t[0], &m);


	t[1].rx_buf = sc16is752->buf;
	t[1].len = len;
	spi_message_add_tail(&t[1], &m);
	
	spin_lock_irq(&sc16is752->spi_lock);
	
	spi_sync(sc16is752->spi, &m);
	
	spin_unlock_irq(&sc16is752->spi_lock);


	printk(KERN_INFO "m.actual_length = %d\n",m.actual_length);
	return m.actual_length;
#endif


	unsigned int length = 0;
	unsigned char status = 0;
	do{
		status = read_752_reg(LSR, sc16is752->channel_num);
		if(status & 0x01)
		{	
			sc16is752->buf[length] = read_752_reg(RHR, sc16is752->channel_num);
			length ++;
		}


	}while(status & 0x01);
	return length;


}


static void write_752_reg(unsigned int reg, unsigned int channel, unsigned char data)
{
	struct spi_transfer t[2];
	struct spi_message m;
	char val = data; 
	
	unsigned char cmd;
	cmd = 0x00 | (reg << 3) | (channel << 1);


	spi_message_init(&m);
	memset(t, 0, (sizeof t));


	t[0].tx_buf = &cmd;
	t[0].len = 1;
	spi_message_add_tail(&t[0], &m);


	t[1].tx_buf = &val;
	t[1].len = 1;
	spi_message_add_tail(&t[1], &m);


	spin_lock_irq(&sc16is752->spi_lock);
	
	spi_sync(sc16is752->spi, &m);


	spin_unlock_irq(&sc16is752->spi_lock);


	return;
	
}


static void write_channel(unsigned char *buf, int len)
{
	struct spi_transfer t[2];
	struct spi_message m;
	unsigned char status;
	unsigned char cmd;
	cmd = 0x00 | (THR << 3) | (sc16is752->channel_num << 1);
	
	status = read_752_reg(LSR, sc16is752->channel_num);
	
	if (status & 0x20)
    	{
		spi_message_init(&m);
		memset(t, 0, (sizeof t));
	
		t[0].tx_buf = &cmd;
		t[0].len = 1;
		spi_message_add_tail(&t[0], &m);
	
		t[1].tx_buf = buf;
		t[1].len = len;
		spi_message_add_tail(&t[1], &m);
		
		spin_lock_irq(&sc16is752->spi_lock);
		
		spi_sync(sc16is752->spi, &m);
	
		spin_unlock_irq(&sc16is752->spi_lock);
	}
	return;
}


static int sc16is752_open(struct inode *inode, struct file *filp)
{
	filp->private_data = sc16is752;


	/*initlizate channel A*/
	write_752_reg(LCR, channelA, 0x80);
	write_752_reg(DLL, channelA, 0x14);
	write_752_reg(DLH, channelA, 0x00);
	write_752_reg(LCR, channelA, 0xbf);
	write_752_reg(EFR, channelA, 0x10);
	write_752_reg(LCR, channelA, 0x03);
	write_752_reg(IER, channelA, 0x01);
	write_752_reg(FCR, channelA, 0xf1);
	//write_752_reg(FCR, channelA, 0x51);
	write_752_reg(SPR, channelA, 0x41);
	write_752_reg(IODIR_752, channelA, 0xff);
	write_752_reg(IOSTATE, channelA, 0x00);


	/*initlizate channel B*/
	write_752_reg(LCR, channelB, 0x80);
	write_752_reg(DLL, channelB, 0x14);
	write_752_reg(DLH, channelB, 0x00);
	write_752_reg(LCR, channelB, 0xbf);
	write_752_reg(EFR, channelB, 0x10);
	write_752_reg(LCR, channelB, 0x03);
	write_752_reg(IER, channelB, 0x01);
	write_752_reg(FCR, channelB, 0xf1);
	//write_752_reg(FCR, channelB, 0x51);
	write_752_reg(SPR, channelB, 0x41);
	write_752_reg(IODIR_752, channelB, 0xff);
	write_752_reg(IOSTATE, channelB, 0x00);


	//request spi buffer
	sc16is752->buf = kmalloc(64,GFP_KERNEL);
	if(!sc16is752->buf)
	{
		printk(KERN_INFO "kzallo buf error\n");
		return -ENOMEM;
	}
	
	nonseekable_open(inode, filp);//设置为不可随机读取。


	printk(KERN_INFO "open and initlizate channel A/B succeed\n ");	
	return 0;	
}


static int sc16is752_release(struct inode *inode, struct file *filp)
{
	if (NULL != sc16is752->buf)
		kfree(sc16is752->buf);
	return 0;	
}


static long sc16is752_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
{
	unsigned char tmp = 0;
	switch (cmd) {
		case SET_channleA:
		{
			sc16is752->channel_num = channelA;
			break;
		}


		case SET_channleB:
		{
			sc16is752->channel_num = channelB;
			break;
		}


		case SET_STOP_BIT:
		{
			tmp = read_752_reg(LCR, sc16is752->channel_num);


			if(1 ==  args)
			{
				tmp &= (~(0x01<<2));//设置1位停止位
				write_752_reg(LCR, sc16is752->channel_num, tmp);
			}
			else if (2 == args)
			{
				tmp |= (0x01<<2);//设置1.5/2位停止位
				write_752_reg(LCR, sc16is752->channel_num, tmp);
			}
			break;
		}


		case SET_BPS:
		{
			tmp = read_752_reg(IER, sc16is752->channel_num);
			tmp &= (~(0x01<<4));
			//禁止睡眠模式才可以设置波特率。
			write_752_reg(IER, sc16is752->channel_num, tmp);
			
			if(9600 == args)
			{	
				write_752_reg(LCR, sc16is752->channel_num, 0x80);
				write_752_reg(DLL, sc16is752->channel_num, 0x14);
				write_752_reg(DLH, sc16is752->channel_num, 0x00);
				write_752_reg(LCR, sc16is752->channel_num, 0xbf);
				write_752_reg(EFR, sc16is752->channel_num, 0x10);
				write_752_reg(LCR, sc16is752->channel_num, 0x03);
				write_752_reg(IER, sc16is752->channel_num, 0x01);
				write_752_reg(FCR, sc16is752->channel_num, 0xf1);
				write_752_reg(SPR, sc16is752->channel_num, 0x41);
				write_752_reg(IODIR_752, sc16is752->channel_num, 0xff);
				write_752_reg(IOSTATE, sc16is752->channel_num, 0x00);
			}
			else if(19200 == args)
			{
				write_752_reg(LCR, sc16is752->channel_num, 0x80);
				write_752_reg(DLL, sc16is752->channel_num, 0x0a);
				write_752_reg(DLH, sc16is752->channel_num, 0x00);
				write_752_reg(LCR, sc16is752->channel_num, 0xbf);
				write_752_reg(EFR, sc16is752->channel_num, 0x10);
				write_752_reg(LCR, sc16is752->channel_num, 0x03);
				write_752_reg(IER, sc16is752->channel_num, 0x01);
				write_752_reg(FCR, sc16is752->channel_num, 0xf1);
				write_752_reg(SPR, sc16is752->channel_num, 0x41);
				write_752_reg(IODIR_752, sc16is752->channel_num, 0xff);
				write_752_reg(IOSTATE, sc16is752->channel_num, 0x00);
			}
			else if(38400 == args)
			{
				write_752_reg(LCR, sc16is752->channel_num, 0x80);
				write_752_reg(DLL, sc16is752->channel_num, 0x05);
				write_752_reg(DLH, sc16is752->channel_num, 0x00);
				write_752_reg(LCR, sc16is752->channel_num, 0xbf);
				write_752_reg(EFR, sc16is752->channel_num, 0x10);
				write_752_reg(LCR, sc16is752->channel_num, 0x03);
				write_752_reg(IER, sc16is752->channel_num, 0x01);
				write_752_reg(FCR, sc16is752->channel_num, 0xf1);
				write_752_reg(SPR, sc16is752->channel_num, 0x41);
				write_752_reg(IODIR_752, sc16is752->channel_num, 0xff);
				write_752_reg(IOSTATE, sc16is752->channel_num, 0x00);
			}
			break;
		}


		default:
			break;
	
	}
	
	return 0;
}


static ssize_t sc16is752_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
	struct sc16is752_dev *sc16is752 = filp->private_data;
	int length = 0;
	int missing;
	//wait_event(sc16is752->sc16is752_q,sc16is752->condition);//用此函数会导致进程杀不死。
	wait_event_interruptible(sc16is752->sc16is752_q,sc16is752->condition);
	
	mutex_lock(&sc16is752->lock);
	length = read_channel(size);
	missing = copy_to_user(buf, sc16is752->buf, length);
	if (missing == length)
		length = -EFAULT;
	else
		length = length - missing;
	mutex_unlock(&sc16is752->lock);


	sc16is752->condition = 0;
	return length;
}


static ssize_t sc16is752_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
	int missing ;
	struct sc16is752_dev *sc16is752 = filp->private_data;
	if(size > 64)
		return -EMSGSIZE;
	
	mutex_lock(&sc16is752->lock);
	missing = copy_from_user(sc16is752->buf, buf, size);
	write_channel(sc16is752->buf, size);
	mutex_unlock(&sc16is752->lock);
	
	return 0;
}


struct file_operations sc16is752_fops = {
    .owner = THIS_MODULE,
    .open = sc16is752_open,
    .write = sc16is752_write,
    .read = sc16is752_read,
    .unlocked_ioctl = sc16is752_ioctl,
    .release = sc16is752_release,
};




struct miscdevice sc16is752_misc = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = "sc16is752",
	.fops = &sc16is752_fops,
}; 




void sc752_work_func(struct work_struct *work)
{
	unsigned char tmp = 0;
	tmp = read_752_reg(IIR, sc16is752->channel_num);
	printk(KERN_INFO "enter sc752_work_func\n");
	if(tmp & 0x04)
	{
		sc16is752->condition = 1;
		wake_up(&sc16is752->sc16is752_q);
	}
}


irqreturn_t sc16is752_handler(int irq, void *dev_id)
{
	schedule_work(sc16is752->sc752_irq_work);
	printk(KERN_INFO "enter sc16is752_handler\n");
	return IRQ_HANDLED;
}


static int sc16is752_probe(struct spi_device *spi)
{
	int ret = 0;
	
	//printk(KERN_INFO "Spi device sc16is752 is probed!\n");
	
	sc16is752 = kzalloc(sizeof(*sc16is752), GFP_KERNEL);
	if (!sc16is752)
		return -ENOMEM;
	sc16is752->channel_num = channelA;//默认使用channelA
	sc16is752->buf = NULL;
	sc16is752->irq = spi->irq;	//中断
	sc16is752->spi = spi;
	spin_lock_init(&sc16is752->spi_lock);
	mutex_init(&sc16is752->lock);
	ret = misc_register(&sc16is752_misc);
	if (ret != 0) {
		printk(KERN_INFO "cannot register miscdev err = %d\n", ret);
	}
	//printk(KERN_INFO "sc16is752->irq %d\n",sc16is752->irq);
	ret = request_threaded_irq(sc16is752->irq, NULL, sc16is752_handler,
		IRQF_TRIGGER_FALLING, "sc16is752", NULL);
	if (ret < 0 )
	{	
		printk(KERN_INFO  "Failed to request IRQ!\n");
	}


	sc16is752->sc752_irq_work = kzalloc(sizeof(struct work_struct),GFP_KERNEL);
	INIT_WORK(sc16is752->sc752_irq_work, sc752_work_func);
	init_waitqueue_head(&sc16is752->sc16is752_q);
	sc16is752->condition = 0;//初始化等待条件为0
	
	spi_set_drvdata(spi, sc16is752);


	return 0;
}


static int __devexit sc16is752_remove(struct spi_device *spi)
{
	misc_deregister(&sc16is752_misc);
	free_irq(sc16is752->irq,NULL);
	kfree(sc16is752);
	return 0;
}


static struct spi_driver sc16is752_driver = {
	.driver = {
		.name		= "sc16is752",
		.owner		= THIS_MODULE,
	},
	.probe		= sc16is752_probe,
	.remove		= __devexit_p(sc16is752_remove),
};


static int __init sc16is752_init(void)
{
	return spi_register_driver(&sc16is752_driver);
}


static void __exit sc16is752_exit(void)
{
	spi_unregister_driver(&sc16is752_driver);
}


module_init(sc16is752_init);
module_exit(sc16is752_exit);


MODULE_DESCRIPTION("Driver for most SPI SC16IS752");
MODULE_AUTHOR("tianyu");
MODULE_LICENSE("GPL");
MODULE_ALIAS("sct");
上面代码注册了一个spi驱动,然后注册一个混杂设备驱动,实现了常用的系统调用。代码运行成功后,会在板子的/dev/目录下,生成一个sc16is752设备文件。打开该设备,是进行设备的初始化,将752设置为中断模式、1位停止位、波特率9600、无奇偶校验、8位数据位。
当应用程序去read sc16is752时,会判断buf中是否接收到了数据,如果没有数据,则让进程休眠,如果有数据,则通过spi接口读取752 fifo中的数据。
752 fifo中一旦接收到了数据,就会产生中断,在中断处理函数中,提交了一个工作,然后便返回,将复杂的中断处理过程留给工作队列去处理。sc752_work_func将处理下半部分工作,在sc752_work_func中,我们先去读取752的IIR寄存器,判断是不是接收数据FIFO中断,然后把唤醒条件置1,并且唤醒工作队列。唤醒进程之后,便继续执行read函数。

下面分析一些比较重要的代码。
struct sc16is752_dev {
	struct spi_device *spi;//用于存放spi设备结构
	spinlock_t spi_lock;//定义自旋锁
	struct mutex lock;//互斥锁
	unsigned char *buf;//spi设备缓冲区,之后会申请为64字节
	unsigned int channel_num;//设备的通道号,表示我要操作那个通道
	int irq;//中断号
	struct work_struct *sc752_irq_work;//工作队列
	wait_queue_head_t sc16is752_q;//进程等待队列
	unsigned int condition;//唤醒条件
};

为了方便操作该设备,我们定义一个结构体来描述这个设备的一些基本特性,然后声明一个struct sc16is752_dev类型全局指针。Probe函数中,将对这个全局指针进行初始化和赋值。
static int sc16is752_probe(struct spi_device *spi)
{
	int ret = 0;
	
	//printk(KERN_INFO "Spi device sc16is752 is probed!\n");
	
	sc16is752 = kzalloc(sizeof(*sc16is752), GFP_KERNEL);
	if (!sc16is752)
		return -ENOMEM;
	sc16is752->channel_num = channelA;//默认使用channelA
	sc16is752->buf = NULL;
	sc16is752->irq = spi->irq;	//中断
	sc16is752->spi = spi;
	spin_lock_init(&sc16is752->spi_lock);
	mutex_init(&sc16is752->lock);
	ret = misc_register(&sc16is752_misc);
	if (ret != 0) {
		printk(KERN_INFO "cannot register miscdev err = %d\n", ret);
	}
	//printk(KERN_INFO "sc16is752->irq %d\n",sc16is752->irq);
	ret = request_threaded_irq(sc16is752->irq, NULL, sc16is752_handler,
		IRQF_TRIGGER_FALLING, "sc16is752", NULL);
	if (ret < 0 )
	{	
		printk(KERN_INFO  "Failed to request IRQ!\n");
	}


	sc16is752->sc752_irq_work = kzalloc(sizeof(struct work_struct),GFP_KERNEL);
	INIT_WORK(sc16is752->sc752_irq_work, sc752_work_func);
	init_waitqueue_head(&sc16is752->sc16is752_q);
	sc16is752->condition = 0;//初始化等待条件为0
	
	spi_set_drvdata(spi, sc16is752);


	return 0;
}

Probe函数中,首先使用kzalloc,分配一个struct sc16is752_dev空间,kzallc与kmallc的区别在于kzalloc会对这个空间进行初始化为0的操作,其它特性一模一样。
sc16is752->irq = spi->irq;获取中断号,中断号在板级文件中定义了,.irq = AT91_PIN_PB18, 
sc16is752->spi = spi;将spi的结构赋值给sc16is752全局指针,后面我们就可以利用sc16is752->spi方便的调用spi core层提供的函数了。
ret = misc_register(&sc16is752_misc);用于注册一个混杂设备,这个混杂设备采用自动分配次设备号。
ret = request_threaded_irq(sc16is752->irq, NULL, sc16is752_handler,
IRQF_TRIGGER_FALLING, "sc16is752", NULL);申请中断,因为752 FIFO中有数据是低电平的,所以采用下降沿触发中断的方式。
sc16is752->sc752_irq_work = kzalloc(sizeof(struct work_struct),GFP_KERNEL);分配一个工作结构体空间。
INIT_WORK(sc16is752->sc752_irq_work, sc752_work_func);初始化工作。
init_waitqueue_head(&sc16is752->sc16is752_q);初始化工作队列。
sc16is752->condition = 0;初始化等待条件为0
spi_set_drvdata(spi, sc16is752); 用来存储驱动中要用到的私有数据

sc16is752_misc结构:
struct miscdevice sc16is752_misc = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = "sc16is752",
	.fops = &sc16is752_fops,
};

混杂设备操作函数集:
struct file_operations sc16is752_fops = {
    .owner = THIS_MODULE,
    .open = sc16is752_open,
    .write = sc16is752_write,
    .read = sc16is752_read,
    .unlocked_ioctl = sc16is752_ioctl,
    .release = sc16is752_release,
};

sc16is752_open函数主要是进行752的初始化、将全局指针sc16is752复制给设备的私有数据filp->private_data = sc16is752,然后给spi申请一个64byte的缓存空间sc16is752->buf = kmalloc(64,GFP_KERNEL);最后将此设备文件设置为不可随机读取nonseekable_open(inode, filp)。
sc16is752_release函数就是释放了申请的64byte的buf缓冲区。
sc16is752_ioctl实现了通道的设置、波特率设置、停止位设置。需要注意波特率设置那一块,通过实践发现,不能够直接写DLL和DLH去设置波特率,会不成功。:
write_752_reg(DLL, sc16is752->channel_num, 0x14);
write_752_reg(DLH, sc16is752->channel_num, 0x00);

需要做一些初始化的步骤:
write_752_reg(LCR, sc16is752->channel_num, 0x80);
write_752_reg(DLL, sc16is752->channel_num, 0x14);//设置波特率。
write_752_reg(DLH, sc16is752->channel_num, 0x00);
write_752_reg(LCR, sc16is752->channel_num, 0xbf);
write_752_reg(EFR, sc16is752->channel_num, 0x10);
write_752_reg(LCR, sc16is752->channel_num, 0x03);
write_752_reg(IER, sc16is752->channel_num, 0x01);
write_752_reg(FCR, sc16is752->channel_num, 0xf1);
write_752_reg(SPR, sc16is752->channel_num, 0x41);
write_752_reg(IODIR_752, sc16is752->channel_num, 0xff);
write_752_reg(IOSTATE, sc16is752->channel_num, 0x00);

接下来分析write函数。
static ssize_t sc16is752_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
	int missing ;
	struct sc16is752_dev *sc16is752 = filp->private_data;
	if(size > 64)
		return -EMSGSIZE;
	
	mutex_lock(&sc16is752->lock);
	missing = copy_from_user(sc16is752->buf, buf, size);
	write_channel(sc16is752->buf, size);
	mutex_unlock(&sc16is752->lock);
	
	return 0;
}

copy_from_user(sc16is752->buf, buf, size);将应用层的buf指针当中size空间的数据复制到sc16is752设备中的buf空间中。注意size是应用空间write传下来的。如:write(fd,”hello”, 6);把hello 6字节数据传到sc16is752->buf中,hello后面还一个‘\0’字符哦。
write_channel(sc16is752->buf, size);将hello字符,写到752中,打开write_channel函数:
static void write_channel(unsigned char *buf, int len)
{
	struct spi_transfer t[2];
	struct spi_message m;
	unsigned char status;
	unsigned char cmd;
	cmd = 0x00 | (THR << 3) | (sc16is752->channel_num << 1);
	
	status = read_752_reg(LSR, sc16is752->channel_num);
	
	if (status & 0x20)
    	{
		spi_message_init(&m);
		memset(t, 0, (sizeof t));
	
		t[0].tx_buf = &cmd;
		t[0].len = 1;
		spi_message_add_tail(&t[0], &m);
	
		t[1].tx_buf = buf;
		t[1].len = len;
		spi_message_add_tail(&t[1], &m);
		
		spin_lock_irq(&sc16is752->spi_lock);
		
		spi_sync(sc16is752->spi, &m);
	
		spin_unlock_irq(&sc16is752->spi_lock);
	}
	return;
}

以hello字符串分析,参数 buf指向sc16is752->buf空间,也就是存放hello字符串的内核空间,参数len就是应用空间传过来的6。函数开始读取752的LSR寄存器,判断发送FIFO是否为空,如果为空,就开始构造spi_message,根据752 spi时序图可以知道,需要先写一个字节,告诉从设备我主设备是想读还是想写,想操作那个寄存器,想操作哪个通道。
cmd = 0x00 | (THR << 3) | (sc16is752->channel_num << 1);表示写,操作THR寄存器,操作的通道由ioctl设置,未设置的情况下是channelA通道。
t[0].tx_buf存放的是操作码,操作码长度为1个字节,t[1].tx_buf存放的是sc16is752->buf空间“hello”字符串,长度就是6。构造好了spi_message之后,就通过spicore层函数spi_sync(sc16is752->spi, &m);将数据发送出去。
Spi_message中有一个actual_length成员变量,actual_length成员变量表示spi实际发送的字节数,也就是t[0].len+t[1].len = len的值,也就是7.

接下来分析sc16is752_read函数:
static ssize_t sc16is752_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
	struct sc16is752_dev *sc16is752 = filp->private_data;
	int length = 0;
	int missing;
	//wait_event(sc16is752->sc16is752_q,sc16is752->condition);//用此函数会导致进程杀不死。
	wait_event_interruptible(sc16is752->sc16is752_q,sc16is752->condition);
	
	mutex_lock(&sc16is752->lock);
	length = read_channel(size);
	missing = copy_to_user(buf, sc16is752->buf, length);
	if (missing == length)
		length = -EFAULT;
	else
		length = length - missing;
	mutex_unlock(&sc16is752->lock);


	sc16is752->condition = 0;
	return length;
}

开始时调用wait_event_interruptible,当spi接收的数据位空时,wait_event_interruptible让应用程序进入等待队列中休眠,最好不要用wait_event,因为wait_event是不可中断的休眠。
length = read_channel(size);将读取的数据放到sc16is752->buf空间,然后通过copy_to_user,将数据传给应用空间。注意这个当中的length变量,它是实际读取到spi字节数的长度,是需要非常精确的返回给read的,应用空间read成功之后返回的是读取到的字节数!
读取完毕之后,就将等待条件sc16is752->condition设置为0,这样下次读取数据的时候,sc16is752->buf空间为空时,再次让进程进入休眠状态。
Read_channel函数,非常纠结的函数!!
static ssize_t read_channel(int len)
{
#if 0
	struct spi_transfer t[2];
	struct spi_message m;
	unsigned char cmd;
	
	cmd = 0x80 | (THR << 3) | (sc16is752->channel_num<< 1);
	printk(KERN_INFO "enter read_channel\n");
	spi_message_init(&m);
	memset(t, 0, (sizeof t));
	
	t[0].tx_buf = &cmd;
	t[0].len = 1;
	spi_message_add_tail(&t[0], &m);


	t[1].rx_buf = sc16is752->buf;
	t[1].len = len;
	spi_message_add_tail(&t[1], &m);
	
	spin_lock_irq(&sc16is752->spi_lock);
	
	spi_sync(sc16is752->spi, &m);
	
	spin_unlock_irq(&sc16is752->spi_lock);


	printk(KERN_INFO "m.actual_length = %d\n",m.actual_length);
	return m.actual_length;
#endif


	unsigned int length = 0;
	unsigned char status = 0;
	do{
		status = read_752_reg(LSR, sc16is752->channel_num);
		if(status & 0x01)
		{	
			sc16is752->buf[length] = read_752_reg(RHR, sc16is752->channel_num);
			length ++;
		}


	}while(status & 0x01);
	return length;


}

当时想一次性把数据读取出来,这样效率快啊,可是,并不知道串口发了多少字节的数据来,所以也无法正确的把接收到的字节长度传送到应用空间。应用空间读取的数据就会是这样:

SC16IS752驱动编写与调试记录_第2张图片

 
多了很多乱码的,这就是因为一次性把数据读取出来,无法判断串口接收到数据的自己长度造成的。
#if 0 注释掉的就是一次性读取数据的代码。
后面的代码是一个一个字节读取,然后查询LSR寄存器的bit1位,直到FIFO全部读完,每读取一个自己数据,length就加一,这样就能知道串口接收到的数据长度了,并且返回给应用空间。虽然效率低一点,但是保证了驱动的正确性。
正确读取的效果:
SC16IS752驱动编写与调试记录_第3张图片 

补充:
 出现如上的错误是write_channel函数中t[1].len没有初始化造成的,因为没有设置为应用空间传进来的长度,这里面是个垃圾值,spi_sync独占了资源导致内核崩溃。
SC16IS752驱动编写与调试记录_第4张图片


通过串口更新内核方法,uboot下操作:
loadb 0x22000000

nand erase 0x200000 0x300000

nand write 0x22000000 0x200000 0x300000

附测试代码:

#include 
#include 
#include 
#include 
#include 

#define SC752 "/dev/sc16is752"


#define SET_channleA _IOW('k', 0, int)
#define SET_channleB _IOW('k', 1, int)
#define SET_STOP_BIT _IOW('k', 2, int)
#define SET_BPS _IOW('k', 3, int)

#define STOP_BIT_1 0x01
#define STOP_BIT_2 0x02
/**************************************************/
/*  说明:752可以设置1 1.5 2 位停止位,默认设置为1个停止位,字长度默认为8,且不可修改 */
/*  默认波特率设置为9600  */
/*  0-1个停止位(字长度=5,6,7,8) */
/*  1.5个停止位(字长度=5)*/
/*  2个停止位 */
/**************************************************/

int main(void)
{
	char buf[64] = {0};
	int length = 0;
	int i = 0;
	
	int fd = open(SC752, O_RDWR);
	
	if (fd < 0) {
	    printf("Open %s failure.\n", SC752);
	    return -1;
	}

	int rate;
	char c;
	while(1)
	{
		/*默认是通道A,波特率9600,停止位1位*/
		printf("this is default test,stop bit is 1,Baud rate is 9600\n");
		write(fd, "hello", 6);
		sleep(1);
		length = read(fd, buf, 30);
		printf("read length %d. read spi_uart is :%s\n",length, buf);
		memset(buf, 0, 64);
		sleep(1);
	}
#if 0	
	/*设置通道测试*/
	printf("then test set channel,please input a or b\n");
	scanf("%c", &c);
	if (c == 'a')
	{
		ioctl(fd, SET_channleA);	
	}
	else if(c == 'b')
	{
		ioctl(fd, SET_channleB);
	}
	write(fd, "hello", 6);
	sleep(1);
	length = read(fd, buf, 30);
	printf("read length %d. read spi_uart is :%s\n",length, buf);
	memset(buf, 0, 64);
	sleep(1);
	
	/*设置停止位测试*/
	printf("then test set stop bits,please input 1 or 2");
	scanf("%d", &c);
	if(c == 1)
	{
		ioctl(fd, SET_STOP_BIT, STOP_BIT_1);
	}
	else if(c == 2)
	{
		ioctl(fd, SET_STOP_BIT, STOP_BIT_2);	
	}
	write(fd, "hello", 6);
	sleep(1);
	length = read(fd, buf, 30);
	printf("read length %d. read spi_uart is :%s\n",length, buf);
	memset(buf, 0, 64);
	sleep(1);
	


	while(1)
	{
		/*设置波特率测试,设置波特率之后,停止位变为默认的1位,*/
		printf("then test set baud rate,please input 9600 19200 or 38400\n");	
		scanf("%d", &rate);
		ioctl(fd, SET_BPS, rate);
		write(fd, "hello", 6);
		sleep(1);
		length = read(fd, buf, 30);
		printf("read length %d\n",length);
		for(i = 0;i


你可能感兴趣的:(linux驱动)