Linux device driver Inquiry 1th Day----SPI driver (1)

Source: Internet
Author: User
Tags mutex

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-&GT;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-&GT;RX_BUF) {//Receive buffer pointer is not empty K_TMP-&GT;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-&GT;TX_BUF) {///send buffer pointer is not empty K_TMP-&GT;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-&GT;SPI-&GT;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-&GT;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-&GT;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-&GT;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-&GT;SPI = null;//empty spidev_data-> SPI Pointer Spi_set_drvdata (SPI, NULL);//SPI-&GT;DEV-&GT;P-&GT;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-&GT;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)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.