This article allows reprint, please specify the source:Http://blog.csdn.net/fulinus
The Linux kernel code is too big, a small module will let you unprepared, this afternoon resolved to take the SPI drive a good look.
First analyze the spidev.c file, which defines the members in the struct file_operations structure. Members have Spidev_write, Spidev_read, and Spidev_ioctl, and the first two implement half-duplex communication, which implements full-duplex communication. Of course there are open and release and other related members, first ignore it.
Spidev_write-------->spidev_sync
Spidev_write-------->spidev_sync
Spidev_ioctl ------> Spidev_message------->spidev_sync
See the detailed documentation: Click to open link
There are also functions that populate the struct spi_drive members of the data structure body, with spidev_probe,spidev_remove functions. and device-driven initialization and exit functions: Spidev_init and Spidev_exit
Code Analysis See: Click to open the link
Reference:
#include <linux/init.h> #include <linux/module.h> #include <linux/ioctl.h> #include <linux/fs.h > #include <linux/device.h> #include <linux/err.h> #include <linux/list.h> #include <linux/ errno.h> #include <linux/mutex.h> #include <linux/slab.h> #include <linux/spi/spi.h> #include <linux/spi/spidev.h> #include <asm/uaccess.h> #define SPIDEV_MAJOR153//SPIDEV main device number # define N_SPI_MINORS32 /* */static Declare_bitmap (minors, n_spi_minors);//Declare secondary device bitmap # define SPI_MODE_MASK (spi_cpha| spi_cpol| spi_cs_high| Spi_lsb_first| spi_3wire| spi_loop| spi_no_cs| Spi_ready) struct Spidev_data {dev_tdevt;//device number spinlock_tspi_lock;//spin lock struct SPI_DEVICE*SPI;//SPI device structure struct LIST_ Headdevice_entry;struct mutexbuf_lock;//Mutex unsignedusers;//consumer count u8*buffer;//buffer};static LIST_HEAD (device_list); /Declare SPI device list static Define_mutex (device_list_lock);//define mutex static unsigned bufsiz = 4096;//Maximum transfer buffer size Module_param (bufsiz , uint, s_irugo); Module_parm_desc (Bufsiz, "DATA bytes in biggest supported SPI message "), static void Spidev_complete (void *arg) {complete (arg);//Call Complete}static ssize_t spidev_sync (struct spidev_data *spidev, struct spi_message *message) {declare_completion_onstack (done); int Status;message->complete = spidev_complete;//Sets the Complete method callback function for the SPI message Message->context = &done;spin_lock_ IRQ (&spidev->spi_lock); if (Spidev->spi = = NULL)//Determine if there is a specified SPI device status =-eshutdown;elsestatus = Spi_async ( SPIDEV->SPI, message),//spi asynchronous Synchronous Spin_unlock_irq (&spidev->spi_lock), if (status = = 0) {wait_for_completion ( &done)///wait for transmission to complete status = message->status;//get SPI message transport transaction status if (status = = 0) status = message->actual_length;// Status equals the actual length of the transfer}return status;//returns the actual transmission length}static inline ssize_t spidev_sync_write (struct spidev_data *spidev, size_t Len) {struct Spi_transfert = {. tx_buf= spidev->buffer,//send buffer. len= len,//Send data length};struct Spi_messagem;spi_message_ Init (&m);//Initialize SPI message (Initialize SPI delivery transaction queue) Spi_message_add_tail (&t, &m);//AddThe SPR is passed to the queue return Spidev_sync (Spidev, &m);//synchronous read-write}static inline ssize_t spidev_sync_read (struct spidev_data *spidev, size_t len) {struct Spi_transfert = {. rx_buf= spidev->buffer,//receive buffer. len= len,//Receive data length};struct Spi_messagem;spi_ Message_init (&m);//Initialize SPI message (Initialize SPI delivery transaction queue) Spi_message_add_tail (&t, &m);//Add SPR to pass to the queue return spidev_ Sync (Spidev, &m);//synchronous read/write}static ssize_t spidev_read (struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {s Truct spidev_data*spidev;ssize_tstatus = 0;if (Count > Bufsiz)//transmit data greater than buffer capacity Return-emsgsize;spidev = filp-> private_data;//get Spidev_datamutex_lock (&spidev->buf_lock) from file private data pointer;//on Mutex status = Spidev_sync_read (Spidev , count);//synchronous read, returns the length of the transmitted data if (Status > 0) {unsigned longmissing;//the number of lost data missing = Copy_to_user (buf, Spidev->buffer, Status)///Kernel space copy to user space if (missing = = status)//The number of missing data equals the number of data to be transferred status =-efault;elsestatus = status-missing;// The number of successfully transmitted data}mutex_unlock (&spidev->buf_lock);//The Mutex return status;//returns the data that was successfully readNumber}static ssize_t spidev_write (struct file *filp, const char __user *buf,size_t count, loff_t *f_pos) {struct SPIDEV_DATA*SP Idev;ssize_tstatus = 0;unsigned longmissing;if (Count > Bufsiz)//transmit data greater than buffer capacity Return-emsgsize;spidev = filp-> private_data;//get Spidev_datamutex_lock (&spidev->buf_lock) from file private data pointer;//on Mutex missing = Copy_from_user (spidev- >buffer, buf, count);//user space is copied to kernel space if (missing = = 0) {//Transmit failure number is 0status = Spidev_sync_write (Spidev, count);//Synchronous Write, Returns the transmitted data length} Elsestatus =-efault;mutex_unlock (&spidev->buf_lock);//de-Mutex return status;//returns the actual number of write data}static int Spidev_message (struct spidev_data *spidev,struct spi_ioc_transfer *u_xfers, unsigned n_xfers) {struct spi_messagemsg; struct spi_transfer*k_xfers;struct spi_transfer*k_tmp;struct spi_ioc_transfer *u_tmp;unsignedn, total;u8*buf; Intstatus =-efault;spi_message_init (&msg);//Initialize SPI message (Initialize SPI delivery transaction queue) K_xfers = Kcalloc (n_xfers, sizeof (*K_TMP), Gfp_kernel);//Allocate SPI transmit pointer memory if (k_xfers = = NULL) Return-enomem;buf = spidev->buffer;//Gets the buffer of spidev_data total = 0;//n=xfers is the number of spi_ioc_transfer, u_tmp = U_xfers is the Spi_ioc_transfer pointer to be processed for (n = n_xfers, k_tmp = k_xfers, u_tmp = U_xfers;n;n--, k_tmp++, u_tmp++) {K_tmp->len = u_tmp->len;//sets the length of the transmission information total + = k_tmp->len;//Cumulative The total length of the transmitted information if (totals > Bufsiz) {//information exceeds BUFSIZ buffer maximum capacity status =-emsgsize;goto done;} if (U_TMP->RX_BUF) {//Receive buffer pointer is not empty K_TMP->RX_BUF = buf;//buffer points to Bufif (!ACCESS_OK (Verify_write, (U8 __user *) (uintptr _t) U_tmp->rx_buf,u_tmp->len)) goto done; The IF (U_TMP->TX_BUF) {///send buffer pointer is not empty K_TMP->TX_BUF = buf;//buffer pointer to Bufif (Copy_from_user (buf, (const U8 __user *) ( uintptr_t) U_tmp->tx_buf,u_tmp->len)//user space to copy data to Bufgoto done;} BUF + = k_tmp->len;//buffer pointer moves the length of a transmitted message K_tmp->cs_change =!! u_tmp->cs_change;//Setting Cs_changek_tmp->bits_per_word = u_tmp->bits_per_word;//setting Bits_per_word A word how many bits k_tmp- >delay_usecs = u_tmp->delay_usecs;//Set delay_usecs millisecond delay k_tmp->speed_hz = u_tmp->speed_hz;//Set speed_hz rate #ifdef verbosedev_dbg (&spidev->sPi->dev, "Xfer len%zd%s%s%s%dbits%u usec%uhz\n", U_tmp->len,u_tmp->rx_buf? "Rx": "", U_tmp->tx_buf? " Tx ":" ", U_tmp->cs_change?" CS ":", U_tmp->bits_per_word?: Spidev->spi->bits_per_word,u_tmp->delay_usecs,u_tmp->speed_hz?: SPIDEV->SPI->MAX_SPEED_HZ); #endifspi_message_add_tail (k_tmp, &msg);//Add SPR to the queue}//for loop is the role of SPI_IOC_ Transfer batch conversion to SPI transfer struct Spi_transfer, then add into SPI delivery transaction queue status = Spidev_sync (Spidev, &msg);//synchronous Read and write if (status < 0) goto Done;buf = spidev->buffer;//Gets the spidev_data buffer pointer for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {//bulk copy from kernel space spi_ioc_t Ransfer to User space if (U_TMP->RX_BUF) {///To determine if there is a receive buffer if (__copy_to_user (U8 __user *) (uintptr_t) U_tmp->rx_buf, buf,u_ Tmp->len) {status =-efault;goto done;}} BUF + = u_tmp->len;//buf pointer position adjustment points to the next spi_ioc_transfer}status = Total;//status equals the actual transmitted data length Done:kfree (k_xfers);//Release K_ Xfersreturn status;//returns the actual transmitted data length}static long spidev_ioctl (struct file *filp, unsigned int cmd, unsigned long ARG) {Interr = 0;intretval = 0;struct spidev_data*spidev;struct spi_device*spi;u32tmp;unsignedn_ioc;struct spi_ioc_ Transfer*ioc;if (_ioc_type (cmd) = spi_ioc_magic)//Determine the type of control command return-enotty;if (_ioc_dir (cmd) & _ioc_read)// Determines whether the direction of the control command is read Readerr =!ACCESS_OK (Verify_write, (void __user *) arg, _ioc_size (cmd));//Determine the transmit data size if (err = = 0 && _ Ioc_dir (cmd) & _ioc_write)//Determines whether the direction of the control command is write Writeerr =!access_ok (Verify_read, (void __user *) arg, _ioc_size (cmd));// Determine the transfer data size if (err) Return-efault;spidev = filp->private_data;//Get SPIDEV_DATASPIN_LOCK_IRQ from file private data (&spidev- >spi_lock);//spin-lock SPI = Spi_dev_get (SPIDEV->SPI);//Get SPI Device SPIN_UNLOCK_IRQ (&spidev->spi_lock);// Spin lock if (SPI = = NULL)//Get SPI device failed return-eshutdown;//return error mutex_lock (&spidev->buf_lock);//upper Mutex switch (CMD) { Case spi_ioc_rd_mode://Set SPI read mode retval = __put_user (Spi->mode & Spi_mode_mask, (__u8 __user *) arg); Break;case SPI _ioc_rd_lsb_first://set SPI read least significant bit retval = __put_user ((Spi->mode & Spi_lsb_first)? 1 : 0, (__u8 __user *) arg); Break;case spi_ioc_rd_bits_per_word://set SPI read each word with multiple bits retval = __put_user (spi->bits_per_ Word, (__u8 __user *) arg), Break;case spi_ioc_rd_max_speed_hz://set SPI read max rate retval = __put_user (Spi->max_speed_hz, (_ _U32 __user *) arg); Break;case spi_ioc_wr_mode://set SPI write mode retval = __get_user (tmp, (U8 __user *) arg); if (retval = = 0) {U8sav E = spi->mode;//Get SPI device mode if (TMP & ~spi_mode_mask) {retval =-einval;break;} TMP |= Spi->mode & ~spi_mode_mask;spi->mode = (U8) Tmp;retval = Spi_setup (SPI);//Configure SPI Device if (retval < 0) SPI-&G T;mode = save;elsedev_dbg (&spi->dev, "SPI mode%02x\n", TMP);} Break;case spi_ioc_wr_lsb_first://Set SPI Write least significant bit retval = __get_user (tmp, (__u8 __user *) arg); if (retval = = 0) {U8save = spi-& gt;mode;//gets the SPI device mode if (TMP) Spi->mode |= spi_lsb_first;elsespi->mode &= ~spi_lsb_first;retval = Spi_setup ( SPI);//Configure SPI Device if (retval < 0) Spi->mode = save;elsedev_dbg (&spi->dev, "%CSB first\n", tmp? ' l ': ' m ');} Break;case Spi_ioc_wr_bits_per_word://settings SPI write each word with multiple bits retval = __get_user (tmp, (__u8 __user *) arg);//user space Get data if (retval = = 0) {U8save = Spi->b its_per_word;//get the SPI device each word contains how many bits Spi->bits_per_word = tmp;//update the new SPI device with how many bits retval = Spi_setup (SPI),//Configure the SPI device if ( retval < 0)//configuration Failed Spi->bits_per_word = save;//Restore SPI device How many bits per word are included elsedev_dbg (&spi->dev, "%d bits per word\n", TMP);} Break;case spi_ioc_wr_max_speed_hz://Set SPI write maximum rate retval = __get_user (tmp, (__u32 __user *) arg);//user space Get data if (retval = = 0) {U32save = spi->max_speed_hz;//get SPI device maximum rate spi->max_speed_hz = tmp;//Update new SPI device maximum rate retval = Spi_setup (SPI);// Configure the SPI device if (retval < 0)//configuration Failed spi->max_speed_hz = save;//restore SPI device maximum rate elsedev_dbg (&spi->dev, "%d Hz (max) \ n", TMP);} The break;default://command must be a write direction command, and the transfer data must be spi_ioc_message () decorated command if (_ioc_nr (cmd)! = _IOC_NR (spi_ioc_message (0)) | | | _ioc_ DIR (cmd) = _ioc_write) {retval =-enotty;break;} TMP = _ioc_size (cmd);//Calculate Transfer data size if (tmp% sizeof (struct spi_ioc_transfer)) = 0) {//Determine if spi_ioc_transfer alignment retval =-EINVAl;break;} N_IOC = tmp/sizeof (struct spi_ioc_transfer);//Calculate the number of Spi_ioc_transfer data if (N_IOC = = 0) BREAK;IOC = Kmalloc (tmp, Gfp_kerne L);//Assign Spi_ioc_transfer pointer IOC memory if (!IOC) {retval =-enomem;break;} if (__copy_from_user (IOC, (void __user *) ARG, TMP)) {//Copy from user space to kernel space kfree (IOC);//Replication failure releases IOC memory retval =-efault;break;} retval = Spidev_message (Spidev, IOC, N_IOC);//spidev Message Processing kfree (IOC);//release of IOC Memory break;} Mutex_unlock (&spidev->buf_lock);//Solution Mutex spi_dev_put (SPI);//Increase the SPI device reference count return retval; static int Spidev_open (struct inode *inode, struct file *filp) {struct spidev_data*spidev;intstatus =-enxio;mutex_lock (& Amp;device_list_lock)///On Mutex list_for_each_entry (Spidev, &device_list, device_entry) {//Traversal device_listif ( Spidev->devt = = Inode->i_rdev) {//Determine the device number to find the corresponding device status = 0;//setting status is 0break;}} if (status = = 0) {//find the corresponding device if (!spidev->buffer) {//spidev_data buffer is empty spidev->buffer = Kmalloc (Bufsiz, Gfp_kernel) ;//allocates memory if (!spidev->buffer) {//also empty dev_dbg (&spidev->spi->dev, "open/enomem\n");//debug status =-enomem;}} if (status = = 0) {//Find the corresponding device Spidev->users++;//spidev_data user count ++filp->private_data = spidev;//spidev_ Data is placed in the file's private Nonseekable_open (Inode, FILP);//Set the file's open mode (the file read/write pointer does not follow the read and write operation)}} elsepr_debug ("Spidev:nothing for minor %d\n ", Iminor (Inode)); Mutex_unlock (&device_list_lock);//access Mutex return status;} static int spidev_release (struct inode *inode, struct file *filp) {struct spidev_data*spidev;intstatus = 0;mutex_lock (& Amp;device_list_lock); Spidev = filp->private_data;//get spidev_datafilp->private_data = NULL;// Clears the file's private data pointer spidev->users--;//the number of users--if (!spidev->users) {//If the number of users is 0intdofree;kfree (spidev->buffer);/ Release Spidev_data buffer Memory Spidev->buffer = null;//Clear Spidev_data buffer pointer Spin_lock_irq (&spidev->spi_lock);// On spin lock Dofree = (Spidev->spi = = NULL);//Determine if the SPI device is unbound with Spidev_data SPIN_UNLOCK_IRQ (&spidev->spi_lock);//Spin lock if (dofree)//Not bundled SPI device Kfree (SPIDEV);//whether Spidev_data memory}mutex_unlock (&device_list_lock); return status;} static const struct File_operations Spidev_fops = {///file operation function set. Owner =this_module,.write =spidev_write,//Write Write.read =spidev_read,// Read Read.unlocked_ioctl = spidev_ioctl,//control ioctl.open =spidev_open,//Open Open.release =spidev_release,// Release Release.llseek =no_llseek,//file pointer move no_llseek means no move};static struct class *spidev_class;static int __devinit spidev_ Probe (struct spi_device *spi) {struct spidev_data*spidev;intstatus;unsigned Longminor;spidev = kzalloc (sizeof (*spidev ), gfp_kernel);//Allocate Spidev_data memory if (!spidev) Return-enomem;spidev->spi = spi;//set SPIDEV_DATA->SPI (SPI device) Spin_ Lock_init (&spidev->spi_lock); Mutex_init (&spidev->buf_lock); Init_list_head (&spidev->device _entry);//Initialize Spidev_data entry list mutex_lock (&device_list_lock); minor = Find_first_zero_bit (minors, n_spi_minors); /Find secondary device bitmap Assign secondary device number if (minor < n_spi_minors) {struct Device *dev;spidev->devt = MKDEV (spidev_major, minor);//Calculate the device number// Create the device/dev/spidev%d.%d (Spidev bus number.) dev = device_create (Spidev_class, &spi->dev, spidev->devt, Spidev, "spidev%d.%d", Spi->master->bus_num, spi->chip_select); status = Is_err (Dev)? Ptr_err (Dev): 0;} else {dev_dbg (&spi->dev, "no minor number available!\n"); status =-enodev;} if (status = = 0) {//Assign device number succeeded Set_bit (minor, minors);//update secondary device bitmap List_add (&spidev->device_entry, &device_list) ;//Add into the device List}mutex_unlock (&device_list_lock); if (status = = 0) spi_set_drvdata (SPI, Spidev);//spi->dev->p- >driver_data=spidev Elsekfree (Spidev); return status;} static int __devexit spidev_remove (struct spi_device *spi) {struct Spidev_data*spidev = Spi_get_drvdata (SPI);// Obtain SPIDEV_DATASPIN_LOCK_IRQ (&spidev->spi_lock) based on SPI device;//spin lock SPIDEV->SPI = null;//empty spidev_data-> SPI Pointer Spi_set_drvdata (SPI, NULL);//SPI->DEV->P->DRIVER_DATA=NULLSPIN_UNLOCK_IRQ (&spidev->spi_ lock);//Spin lock Mutex_lock (&device_list_lock);//On the Mutex lock List_del (&spidev->device_entry);//Delete spidev_ Data Entry list Device_destroy (Spidev_class, spidev->devt);//Destroy/dev/spidev%d.%dclear_bit (MINOR (SPIDEV->DEVT), minors);//clear the secondary device bitmap corresponding bit if (spidev->users = = 0)//The number of users is 0kfree (Spidev);//Release Spidev_data memory Mutex_unlock ( &device_list_lock);//solution mutex return 0;} static struct Spi_driver spidev_spi_driver = {//SPI device driver. Driver = {. Name = "Spidev",. Owner =this_module,},.probe =spidev_ The probe method of the Probe,//spidev (the probe method is called when an SPI device or a board-level device with a modalias domain of "Spidev" is registered). Remove =__devexit_p (Spidev_remove),// Spidev the Remove method};static int __init spidev_init (void)//spidev interface initialization {int status; BUILD_BUG_ON (N_spi_minors > 256);//register character device, master device number spidev_major=153, bundle device operation function set to Spidev_fopsstatus = Register_chrdev ( Spidev_major, "SPI", &spidev_fops), if (status < 0) return Status;spidev_class = Class_create (This_module, "Spidev ");//Create Device Class Spidev_classif (Is_err (Spidev_class)) {Unregister_chrdev (spidev_major, spidev_spi_driver.driver.name); Return Ptr_err (Spidev_class);} Status = Spi_register_driver (&spidev_spi_driver);//Register SPI device driver Spidev_spi_driverif (Status < 0) {Class_destroy ( Spidev_class); Unregister_chrdev (Spidev_major, spidev_spI_driver.driver.name);} return status;} Module_init (spidev_init);//declaration Initialize ingress static void __exit spidev_exit (void)//spidev interface Destroy {Spi_unregister_driver (& Spidev_spi_driver);//Logoff SPI device driver Spidev_spi_driverclass_destroy (spidev_class);//Logoff device class Spidev_classunregister_ Chrdev (Spidev_major, spidev_spi_driver.driver.name);//unregister character device}module_exit (spidev_exit);//Declare Initialize exit Module_author (" Andrea Paterniani, <[email protected]> "); Module_description ("User mode SPI Device Interface"); Module_license ("GPL"); Module_alias ("Spi:spidev");
Here to tidy up the IOCTL command:
spi_ioc_rd_mode//read mode spi_ioc_rd_lsb_first//read lsbspi_ioc_rd_bits_per_word//read how many bits per word spi_ioc_rd_max_speed_hz//read Maximum rate spi_ioc_wr_mode//Write mode spi_ioc_wr_lsb_first//write lsbspi_ioc_wr_bits_per_word//write each word how many bits spi_ioc_wr_max_speed_hz//write Maximum rate Spi_ioc_message (n)//Transmit N Packets
Linux device driver Inquiry 1th Day----SPI driver (1)