Linux Device Driver inquiry 1st days ---- spi Driver (1), 1st days ---- spi
This document allows reprinting. Please indicate the source:Http://blog.csdn.net/fulinus
The Linux kernel code is too big, and a small module will make you feel helpless. This afternoon, I am determined to take a good look at the spi driver.
First, analyze the spidev. c file, which defines the members in the struct file_operations structure. The members include spidev_write, spidev_read, and spidev_ioctl. The first two implement half-duplex communication, and the latter implements full-duplex communication. Of course, there are open and release members. Ignore them first.
Spidev_write --------> spidev_sync
Spidev_write --------> spidev_sync
Spidev_ioctl ------> spidev_message -------> spidev_sync
For more information, see: Click to open the link.
There are also functions that fill in the struct spi_drive data struct, including the spidev_probe and spidev_remove functions. And the device driver initialization and exit functions: spidev_init and spidev_exit
For 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. h> # include <linux/spi/spidev. h> # include <asm/uaccess. h> # define SPIDEV_MAJOR153 // The master device Number of spidev # define N_SPI_MINORS32 /*... up to 256 */static decia RE_BITMAP (minors, N_SPI_MINORS); // device bitmap declaration # define partition (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | percent | SPI_3WIRE | SPI_LOOP | percent | SPI_READY) struct spidev_data {percent; // device No. spinlock_tspi_lock; // spin lock struct spi_device * spi; // spi device struct list_headdevice_entry; struct mutexbuf_lock; // mutex lock unsignedusers; // user count u8 * buffer; // buffer}; static LIST_HEAD (device_list); // declare the static DEFINE_MUTEX (device_l Ist_lock); // defines the mutex lock 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; // set Message-> context = & done; spin_lock_irq (& spidev-> spi_lock); if (spidev-> spi = NULL) // determine whether the specified spi device status is-eshudown; elsestatus = spi_async (spidev-> spi, message ); // spi asynchronous synchronization of spin_unlock_irq (& spidev-> spi_lock); if (status = 0) {wait_for_completion (& done); // wait until the transmission is complete. status = message-> status; // obtain the Transaction status of spi message transmission if (status = 0) status = message-> actual_length; // status is equal to the actual length of transmission} return statu S; // 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, // sending buffer. len = len, // send data length}; struct spi_messagem; spi_message_init (& m); // initialize spi message (initialize spi transfer transaction queue) spi_message_add_tail (& t, & m); // Add spr passed to this queue return spidev_sync (spidev, & m); // synchronous read/write} static inline ssize_t spidev_sync_read (struct spidev_data * spidev, size_t len) {struct s Pi_transfert = {. rx_buf = spidev-> buffer, // receives the buffer. len = len, // The length of the received data}; struct spi_messagem; spi_message_init (& m); // initialize the spi message (initialize the spi transfer transaction queue) spi_message_add_tail (& t, & m); // Add spr passed to this 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) {struct spidev_data * spidev; ssize_tstatus = 0; if (count> bufsiz) // transfer data greater than the buffer capacity return-EMSGSIZE; sp Idev = filp-> private_data; // obtain spidev_datamutex_lock (& spidev-> buf_lock) from the Private Data Pointer of the file; // obtain the mutex lock status = spidev_sync_read (spidev, count ); // synchronous read, return the length of transmitted data if (status> 0) {unsigned longmissing; // The number of lost data missing = copy_to_user (buf, spidev-> buffer, status ); // copy the kernel space to the user space if (missing = status) // The number of lost data equals to the number of data to be transmitted status =-EFAULT; elsestatus = status-missing; // number of successfully transferred data} mutex_unlock (& spidev-> buf_lock); // returns st Atus; // return the number of successfully read data} static ssize_t spidev_write (struct file * filp, const char _ user * buf, size_t count, loff_t * f_pos) {struct spidev_data * spidev; ssize_tstatus = 0; unsigned longmissing; if (count> bufsiz) // transfer data greater than the buffer capacity return-EMSGSIZE; spidev = filp-> private_data; // obtain spidev_datamutex_lock (& spidev-> buf_lock) from the Private Data Pointer of the file; // The Mutual Exclusion lock missing = copy_from_user (spidev-> buffer, buf, count ); // copy the user space to the kernel space if (missing = 0) {// The number of failed transfers is 0 status = spidev_sync_write (spidev, count); // synchronous write, return the length of transmitted data} elsestatus =-EFAULT; mutex_unlock (& spidev-> buf_lock); // unlock mutex lock return status; // returns the actual number of written 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 serial * u_tmp; unsignedn, total; u8 * buf; intst Atus =-EFAULT; spi_message_init (& msg); // initialize the spi message (initialize the spi transfer transaction queue) k_xfers = kcalloc (n_xfers, sizeof (* k_tmp), GFP_KERNEL ); // allocate spi transmission pointer memory if (k_xfers = NULL) return-ENOMEM; buf = spidev-> buffer; // obtain the buffer total of spidev_data = 0; // n = xfers is the number of spi_ioc_transfer and 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 --, k_tmp ++, u_tmp ++) {k_tmp-> len = u_tmp-> len; // you can specify the total length of transmission information. + = K_tmp-> len; // The total length of accumulated transmission information if (total> bufsiz) {// The amount of information exceeds the maximum capacity of the bufsiz buffer status =-EMSGSIZE; goto done ;} if (u_tmp-> rx_buf) {// The receiving buffer pointer is not empty k_tmp-> rx_buf = buf; // The buffer points to bufif (! Access_ OK (VERIFY_WRITE, (u8 _ user *) (uintptr_t) u_tmp-> rx_buf, u_tmp-> len) goto done;} if (u_tmp-> tx_buf) {// The sending buffer pointer is not empty k_tmp-> tx_buf = buf; // The buffer pointer points to bufif (copy_from_user (buf, (const u8 _ user *) (uintptr_t) u_tmp-> tx_buf, u_tmp-> len) // copy data to the bufgoto done;} buf + = k_tmp-> len; // The buffer pointer moves the length of a transmission message k_tmp-> cs_change = !! U_tmp-> cs_change; // set cs_changek_tmp-> bits_per_word = u_tmp-> bits_per_word; // set the number of characters in bits_per_word: k_tmp-> delay_usecs = u_tmp-> delay_usecs; // set delay_usecs millisecond-level latency k_tmp-> speed_hz = u_tmp-> speed_hz; // set the speed_hz rate # ifdef VERBOSEdev_dbg (& spidev-> spi-> dev, "xfer len % zd % 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 passed to this queue} // for Loop function is to batch convert spi_ioc_transfer to spi transfer struct spi_transfer, and then add it to spi transfer transaction queue status = spidev_sync (spidev, & msg); // synchronous read/write if (status <0) goto done; buf = spidev-> buffer; // obtain the spidev_data buffer pointer for (n = n_xfers, u_tmp = u_xfers; n; n --, u_tmp ++) {// copy spi_ioc_transfer to user space in batches if (u_tmp-> rx_buf) {// determine whether the receiving 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; // adjust the position of the buf pointer to the next spi_ioc_transfer} status = total; // status equals to the actual length of the transmitted data done: kfree (k_xfers); // release k_xfersreturn status; // return 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) // determines the type of the 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 size of the transmitted data if (err = 0 & _ IOC_DIR (cmd) & _ IOC_WRITE) // determines whether the direction of the control command is writeerr =! Access_ OK (VERIFY_READ, (void _ user *) arg, _ IOC_SIZE (cmd); // determine the size of the transmitted data if (err) return-EFAULT; spidev = filp-> private_data; // obtain spidev_dataspin_lock_irq (& spidev-> spi_lock) from the private data of the file; // obtain the spin lock spi = spi_dev_get (spidev-> spi ); // retrieve the spi device spin_unlock_irq (& spidev-> spi_lock); // unlock the spin lock if (spi = NULL) // return-eshudown upon obtaining the spi device failure; // The error mutex_lock (& spidev-> buf_lock) is returned. // The mutex lock switch (cmd) {case SPI_IOC_RD_MODE: // set the spi read mode retval = _ Put_user (spi-> mode & SPI_MODE_MASK, (_ u8 _ user *) arg); break; case SPI_IOC_RD_LSB_FIRST: // set the minimum valid position for spi read retval = _ put_user (spi-> mode & SPI_LSB_FIRST )? 1: 0, (_ u8 _ user *) arg); break; case SPI_IOC_RD_BITS_PER_WORD: // set spi to read each word containing multiple individual retval = _ put_user (spi-> bits_per_word, (_ u8 _ user *) arg); break; case SPI_IOC_RD_MAX_SPEED_HZ: // set the maximum spi read rate retval = _ put_user (spi-> max_speed_hz, (_ u32 _ user *) arg); break; case SPI_IOC_WR_MODE: // set the spi write mode retval = _ get_user (tmp, (u8 _ user *) arg); if (retval = 0) {u8save = spi-> mode; // obtain the 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 the spi device if (retval <0) spi-> mode = save; elsedev_dbg (& spi-> dev, "spi mode % 02x \ n", tmp);} break; case SPI_IOC_WR_LSB_FIRST: // set the minimum valid bits for writing spi retval = _ get_user (tmp, (_ u8 _ user *) arg); if (retval = 0) {u8save = spi-> mode; // obtain the spi device mode. if (tmp) spi-> mode | = SPI_LSB_FIRST; elsespi-> mode & = ~ SPI_LSB_FIRST; retval = spi_setup (spi); // configure the 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: // set each word written by spi to contain multiple digits retval = _ get_user (tmp, (_ u8 _ user *) arg); // if (retval = 0) {u8save = spi-> bits_per_word; // obtain the number of bits in each word of the spi device-> bits_per_word = tmp; // update the number of bits in each word of the new spi device retval = spi_setup (spi ); // configure the spi device if (retval <0) // configuration failed spi-> bits_per_word = save; // restore the number of elsedev_dbg (& spi-> dev, "% d bits per word \ n", tmp);} break; case SPI_IOC_WR_MAX _ SPEED_HZ: // set the maximum spi write rate retval = _ get_user (tmp, (_ u32 _ user *) arg ); // if (retval = 0) {u32save = spi-> max_speed_hz; // obtain the maximum rate of the spi device spi-> max_speed_hz = tmp; // update the maximum rate retval = spi_setup (spi) of the new spi device; // configure the spi device if (retval <0) // The configuration fails spi-> max_speed_hz = save; // restore the maximum rate of the spi device elsedev_dbg (& spi-> dev, "% d Hz (max) \ n", tmp);} break; default: // The command must be a write command, and the data transmission must be the command if (_ IOC_NR (cmd) modified by SPI_IOC_MESSAGE )! = _ IOC_NR (SPI_IOC_MESSAGE (0) | _ IOC_DIR (cmd )! = _ IOC_WRITE) {retval =-ENOTTY; break;} tmp = _ IOC_SIZE (cmd); // calculate the transfer data size if (tmp % sizeof (struct spi_ioc_transfer ))! = 0) {// determine whether it is spi_ioc_transfer aligned 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_KERNEL); // allocate the 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); // if the copy fails, the ioc memory retval =-EFAULT; break;} retval = spidev_message (spidev, ioc, n_ioc); // spidev Message Processing kfree (ioc); // releases the ioc memory break;} mutex_unlock (& spidev-> buf_lock); // unmutex lock spi_dev_put (spi ); // Add the reference count return retval;} static int spidev_open (struct inode * inode, struct file * filp) {struct spidev_data * spidev; Intstatus =-ENXIO; mutex_lock (& device_list_lock); // The list_for_each_entry (spidev, & device_list, device_entry) {// traverse device_listif (spidev-> devt = inode-> I _rdev) {// determine whether the device ID finds the corresponding device status = 0; // set the status to 0 break;} if (status = 0) {// find the corresponding device if (! Spidev-> buffer) {// The spidev_data buffer is empty. spidev-> buffer = kmalloc (bufsiz, GFP_KERNEL); // The memory is allocated if (! Spidev-> buffer) {// dev_dbg (& spidev-> spi-> dev, "open/ENOMEM \ n") is empty "); // debug status =-ENOMEM;} if (status = 0) {// find the corresponding device spidev-> users ++; // spidev_data user count + + filp-> private_data = spidev; // put spidev_data in the private data of the file nonseekable_open (inode, filp ); // set the file opening mode (the file read/write pointer will not follow the read/write operation)} elsepr_debug ("spidev: nothing for minor % d \ n", iminor (inode )); mutex_unlock (& device_list_lock); // returns status;} static int spidev_re Lease (struct inode * inode, struct file * filp) {struct spidev_data * spidev; intstatus = 0; mutex_lock (& device_list_lock); spidev = filp-> private_data; // obtain spidev_datafilp-> private_data = NULL; // clear the Private Data Pointer of the file. spidev-> users --; // Number of users -- if (! Spidev-> users) {// if the number of users is 0 intdofree; kfree (spidev-> buffer); // release the buffer memory of spidev_data. spidev-> buffer = NULL; // clear the spidev_data buffer pointer spin_lock_irq (& spidev-> spi_lock); // The spin lock dofree = (spidev-> spi = NULL ); // determine whether the spi device and spidev_data are unbound from the spin_unlock_irq (& spidev-> spi_lock); // unlock the spin lock if (dofree) // The unbound spi device kfree (spidev ); // check whether spidev_data memory} mutex_unlock (& device_list_lock); return status;} static const struct file_operations spi Dev_fops = {// file operation function set. owner = THIS_MODULE ,. write = spidev_write, // write. read = spidev_read, // read. unlocked_ioctl = spidev_ioctl, // control ioctl. open = spidev_open, // open. release = spidev_release, // release. llseek = no_llseek, // The file pointer moves no_llseek to indicate no movement}; static struct class * spidev_class; static int _ devinit spidev_probe (struct spi_device * spi) {struct spidev_data * spidev; intstatus; unsigned longminor; spidev = Kzarloc (sizeof (* spidev), GFP_KERNEL); // allocate the 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 the spidev_data entry linked list mutex_lock (& device_list_lock); minor = minute (minors, N_SPI_MINORS ); // find the device bitmap to assign the 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. part selection) 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) {// The device number is successfully allocated. set_bit (minor, minors ); // update the device bitmap list_add (& spidev-> device_entry, & device_list); // Add it to the device linked 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 the spi device ); // The upper spin lock spidev-> spi = NULL; // clear the spidev_data-> spi pointer spi_set_drvdata (spi, NULL ); // spi-> dev-> p-> driver_data = NULLspin_unlock_irq (& spidev-> spi_lock); // unlock the spin lock mutex_lock (& device_list_lock ); // list_del (& spidev-> device_entry); // Delete the device_destroy (spidev_class, spidev-> devt) linked list of the spidev_data entry; // destroy/dev/spidev % d. % dclear_bit (MINOR (spidev-> devt), minors); // if (spidev-> users = 0) // The number of users is 0 kfree (spidev); // release the spidev_data memory mutex_unlock (& device_list_lock); // unlock the mutex and return 0 ;} static struct spi_driver spidev_spi_driver = {// spi device driver. driver = {. name = "spidev ",. owner = THIS_MODULE ,},. probe = spidev_probe, // probe method of spidev (when an spi device or board-level device registered with the modalias domain "spidev" is called ). remove =__ devexit_p (spidev_remove), // The remove Method of spidev}; static int _ init spidev_init (void) // initialize the spidev interface {int status; BUILD_BUG_ON (N_SPI_MINORS> 256); // registers a character device. The master device number is SPIDEV_MAJOR = 153. The bound device operation function set is spidev_fopsstatus = offline (SPIDEV_MAJOR, "spi", & spidev_fops ); if (status <0) return status; spidev_class = class_create (THIS_MODULE, "spidev"); // create a device class spidev_classif (IS_ERR (spidev_class) {kernel (SPIDEV_MAJOR, kernel ); return PTR_ERR (spidev_class);} status = spi_register_driver (& spidev_spi_driver); // register the spi Device Driver drivers (status <0) {class_destroy (spidev_class); then (SPIDEV_MAJOR, callback);} return status;} module_init (spidev_init); // declare the initialization entry static void _ exit spidev_exit (void) // destroy {spi_unregister_driver (& spidev_spi_driver) through the spidev interface ); // deregister the spi Device Driver spidev_spi_driverclass_destroy (spidev_class); // deregister the device class category (SPIDEV_MAJOR, spidev_spi_driver.driver.name); // deregister the character device} module_exit ); // declare the initialization exit MODULE_AUTHOR ("Andrea Paterniani, <a.paterniani@swapp-eng.it>"); MODULE_DESCRIPTION ("User mode SPI device interface"); MODULE_LICENSE ("GPL"); MODULE_ALIAS ("spi: spidev ");
Here are the ioctl commands:
SPI_IOC_RD_MODE // read mode SPI_IOC_RD_LSB_FIRST // read bytes // number of bytes read per word // read maximum speed SPI_IOC_WR_MODE // write mode SPI_IOC_WR_LSB_FIRST // write bytes // write the number of bytes per word SPI_IOC_WR_MAX_SPEED_HZ // maximum write rate SPI_IOC_MESSAGE (n) // transmit n data packets