Linux Device Driver inquiry 1st days ---- spi Driver (1), 1st days ---- spi

Source: Internet
Author: User

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






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.