링크 ux SPI 구동 프레임 워 크(2)--장치 구동

22653 단어 linuxdriver
전재 출처 floater 의 csdn blog 를 표시 하 십시오.http://blog.csdn.net/flaoter
이전 글 Liux SPI 구동 프레임 워 크(1)-컨트롤 러 구동http://blog.csdn.net/flaoter/article/details/50001133이 절 은 SPI 가 장치 의 장치 구동 에서 설명 하 는 내용 을 소개 합 니 다.
장치 구동 이 주목 하 는 구조 체 는 주로 두 가지 가 있 습 니 다.struct spidevice 설명 spi 장치,struct spidriver 는 장치 의 장치 에서 구동 된다.
struct spi_device {
    struct device       dev;
    struct spi_master   *master;
    u32         max_speed_hz;
    u8          chip_select;
    u8          bits_per_word;
    u16         mode;
#define SPI_CPHA    0x01            /* clock phase */
#define SPI_CPOL    0x02            /* clock polarity */
#define SPI_MODE_0  (0|0)           /* (original MicroWire) */
#define SPI_MODE_1  (0|SPI_CPHA)
#define SPI_MODE_2  (SPI_CPOL|0)
#define SPI_MODE_3  (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH 0x04            /* chipselect active high? */
#define SPI_LSB_FIRST   0x08            /* per-word bits-on-wire */
#define SPI_3WIRE   0x10            /* SI/SO signals shared */
#define SPI_LOOP    0x20            /* loopback mode */
#define SPI_NO_CS   0x40            /* 1 dev/bus, no chipselect */
#define SPI_READY   0x80            /* slave pulls low to pause */
#define SPI_TX_DUAL 0x100           /* transmit with 2 wires */
#define SPI_TX_QUAD 0x200           /* transmit with 4 wires */
#define SPI_RX_DUAL 0x400           /* receive with 2 wires */
#define SPI_RX_QUAD 0x800           /* receive with 4 wires */
    int         irq;
    void            *controller_state;
    void            *controller_data;
    char            modalias[SPI_NAME_SIZE];
    int         cs_gpio;    /* chip select gpio */

    /*
     * likely need more hooks for more protocol options affecting how
     * the controller talks to each chip, like:
     *  - memory packing (12 bit samples into low bits, others zeroed)
     *  - priority
     *  - drop chipselect after each word
     *  - chipselect delays
     *  - ...
     */
};

struct spi_driver {
    const struct spi_device_id *id_table;
    int         (*probe)(struct spi_device *spi);
    int         (*remove)(struct spi_device *spi);
    void            (*shutdown)(struct spi_device *spi);
    int         (*suspend)(struct spi_device *spi, pm_message_t mesg);
    int         (*resume)(struct spi_device *spi);
    struct device_driver    driver;
};

초기 화 함 수 는 다음 과 같 습 니 다.코드 설명 은 코드 설명 을 참조 합 니 다.이 코드 에는 장치 등록 코드 가 포함 되 어 있 지만 dts 가 보편적으로 응 용 된 후에 이 장치 등록 방식 은 장치 트 리 방식 으로 대체 되 었 습 니 다.이곳 의 장치 등록 응용 장면 은 모듈 방식 으로 컴 파일 된 구동 디자인 에 많이 나타 납 니 다.따라서 설비 등록 에 헷 갈 리 지 마 세 요.
static int __init spidev_init(void)
{
    int status;

    /* Claim our 256 reserved device numbers.  Then register a class
     * that will key udev/mdev to add/remove /dev nodes.  Last, register
     * the driver which manages those device numbers.
     */
    BUILD_BUG_ON(N_SPI_MINORS > 256);
//      ,     SPIDEV_MAJOR,    "spi",    ops spidev_fops,   /proc/devices      ,    udev  uevent,   /dev    ,      
    status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);  
    if (status < 0)
        return status;
//  spidev class, /sys/class/spidev
    spidev_class = class_create(THIS_MODULE, "spidev");
    if (IS_ERR(spidev_class)) {
        status = PTR_ERR(spidev_class);
        goto error_class;
    }
//    spidev_spi_driver  
    status = spi_register_driver(&spidev_spi_driver);
    if (status < 0)
        goto error_register;
//    ,     module driver          ,busnum, chipselect   MODULE_PARAM  ;
//     ,            spi_master     , dts       spi slave  spi_device   
    if (busnum != -1 && chipselect != -1) {
        struct spi_board_info chip = {
                    .modalias   = "spidev",        // device_driver  match  
                    .mode       = spimode, //spi    
                    .bus_num    = busnum,          //spi master busnum
                    .chip_select    = chipselect,  
                    .max_speed_hz   = maxspeed,
        };

        struct spi_master *master;

        master = spi_busnum_to_master(busnum);  //  busnum     spi_master
        if (!master) {
            status = -ENODEV;
            goto error_busnum;
        }
//spi slave  spi_device     ,   master slave     
        /* We create a virtual device that will sit on the bus */
        spi = spi_new_device(master, &chip);  
        if (!spi) {
            status = -EBUSY;
            goto error_mem;
        }
        dev_dbg(&spi->dev, "busnum=%d cs=%d bufsiz=%d maxspeed=%d",
            busnum, chipselect, bufsiz, maxspeed);
    }
    return 0;
error_mem:
error_busnum:
    spi_unregister_driver(&spidev_spi_driver);
error_register:
    class_destroy(spidev_class);
error_class:
    unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
    return status;
}
module_init(spidev_init);

다음은 장치 구동 의 정의 입 니 다.compatible 이 장치 등록 속성 과 일치 할 때 spidevprobe 호출 됩 니 다.
static const struct of_device_id spidev_dt_ids[] = {
    { .compatible = "rohm,dh2228fv", },
    { .compatible = "qcom,spi-msm-codec-slave", },
    {},
};

MODULE_DEVICE_TABLE(of, spidev_dt_ids);

static struct spi_driver spidev_spi_driver = {
    .driver = {
        .name =     "spidev",
        .owner =    THIS_MODULE,
        .of_match_table = of_match_ptr(spidev_dt_ids),
    },
    .probe =    spidev_probe,
    .remove =   spidev_remove,

    /* NOTE:  suspend/resume methods are not necessary here.
     * We don't do anything except pass the requests to/from
     * the underlying controller.  The refrigerator handles
     * most issues; the controller driver handles the rest.
     */
};

probe 함수 정 의 는 다음 과 같 습 니 다.
static int spidev_probe(struct spi_device *spi)
{
    struct spidev_data  *spidev;
    int         status;
    unsigned long       minor;

    /* Allocate driver data */
    spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
    if (!spidev)
        return -ENOMEM;

    /* Initialize the driver data */
    spidev->spi = spi;
    spin_lock_init(&spidev->spi_lock);
    mutex_init(&spidev->buf_lock);

    INIT_LIST_HEAD(&spidev->device_entry);

    /* If we can allocate a minor number, hook up this device.
     * Reusing minors is fine so long as udev or mdev is working.
     */
    mutex_lock(&device_list_lock);
//         
    minor = find_first_zero_bit(minors, N_SPI_MINORS);
    if (minor < N_SPI_MINORS) {
        struct device *dev;

        spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
//    ,/sys/class/spidev   spidev%d.%d    ,  udev   /dev        
        dev = device_create(spidev_class, &spi->dev, spidev->devt,     
                    spidev, "spidev%d.%d",
                    spi->master->bus_num, spi->chip_select);
        status = PTR_ERR_OR_ZERO(dev);
    } else {
        dev_dbg(&spi->dev, "no minor number available!
"
); status = -ENODEV; } if (status == 0) { set_bit(minor, minors); list_add(&spidev->device_entry, &device_list); // spidev device_list } mutex_unlock(&device_list_lock); if (status == 0) spi_set_drvdata(spi, spidev); //spi->dev->drvdata=spidev else kfree(spidev); return status; }

파일 조작 함수 정 의 는 다음 과 같 습 니 다.
static const struct file_operations spidev_fops = {
    .owner =    THIS_MODULE,
    /* REVISIT switch to aio primitives, so that userspace
     * gets more complete API coverage.  It'll simplify things
     * too, except for the locking.
     */
    .write =    spidev_write,
    .read =     spidev_read,
    .unlocked_ioctl = spidev_ioctl,
    .compat_ioctl = spidev_compat_ioctl,
    .open =     spidev_open,
    .release =  spidev_release,
    .llseek =   no_llseek,
};

다음은 open 과 write 함 수 를 예 로 들 어 장치 조작 함수 에 대해 설명 합 니 다.
static int spidev_open(struct inode *inode, struct file *filp)
{
    struct spidev_data  *spidev;
    int         status = -ENXIO;

    mutex_lock(&device_list_lock);

    list_for_each_entry(spidev, &device_list, device_entry) {  //    inode->i_rdev device_list     spidev
        if (spidev->devt == inode->i_rdev) {
            status = 0;
            break;
        }
    }

    if (status) {
        pr_debug("spidev: nothing for minor %d
"
, iminor(inode)); goto err_find_dev; } if (!spidev->tx_buffer) { spidev->tx_buffer = kmalloc(bufsiz, GFP_KERNEL); // tx buffer if (!spidev->tx_buffer) { dev_dbg(&spidev->spi->dev, "open/ENOMEM
"
); status = -ENOMEM; goto err_find_dev; } } if (!spidev->rx_buffer) { spidev->rx_buffer = kmalloc(bufsiz, GFP_KERNEL); // rx buffer if (!spidev->rx_buffer) { dev_dbg(&spidev->spi->dev, "open/ENOMEM
"
); status = -ENOMEM; goto err_alloc_rx_buf; } } spidev->users++; // filp->private_data = spidev; // spidev filp nonseekable_open(inode, filp); mutex_unlock(&device_list_lock); return 0; err_alloc_rx_buf: kfree(spidev->tx_buffer); spidev->tx_buffer = NULL; err_find_dev: mutex_unlock(&device_list_lock); return status; }
static ssize_t
spidev_write(struct file *filp, const char __user *buf,
        size_t count, loff_t *f_pos)
{
    struct spidev_data  *spidev;
    ssize_t         status = 0;
    unsigned long       missing;

    /* chipselect only toggles at start or end of operation */
    if (count > bufsiz)
        return -EMSGSIZE;

    spidev = filp->private_data;  //     filp  spidev

    mutex_lock(&spidev->buf_lock);
    missing = copy_from_user(spidev->tx_buffer, buf, count);  //    buf   spidev->tx_buf
    if (missing == 0)
        status = spidev_sync_write(spidev, count); //    spidev_sync_write  
    else
        status = -EFAULT;
    mutex_unlock(&spidev->buf_lock);

    return status;
}

write 함수 의 구체 적 인 실현 은 다음 과 같 습 니 다.
static inline ssize_t
spidev_sync_write(struct spidev_data *spidev, size_t len)
{
    struct spi_transfer t = {
            .tx_buf     = spidev->tx_buffer,
            .len        = len,
        };
    struct spi_message  m;

    spi_message_init(&m);   //   spi_message
    spi_message_add_tail(&t, &m);  // spi_transfer  spi_message   
    return spidev_sync(spidev, &m); //    spidev_sync
}
static ssize_t
spidev_sync(struct spidev_data *spidev, struct spi_message *message)
{
    DECLARE_COMPLETION_ONSTACK(done);
    int status;

    message->complete = spidev_complete;  
    message->context = &done;   //spi_message      

    spin_lock_irq(&spidev->spi_lock);
    if (spidev->spi == NULL)
        status = -ESHUTDOWN;
    else
        status = spi_async(spidev->spi, message);  //  spi_async    
    spin_unlock_irq(&spidev->spi_lock);

    if (status == 0) {
        wait_for_completion(&done);  //     
        status = message->status;
        if (status == 0)
            status = message->actual_length;
    }
    return status;
}

int spi_async(struct spi_device *spi, struct spi_message *message)
{
    struct spi_master *master = spi->master;
    int ret;
    unsigned long flags;

    ret = __spi_validate(spi, message);
    if (ret != 0)
        return ret;

    spin_lock_irqsave(&master->bus_lock_spinlock, flags);

    if (master->bus_lock_flag)
        ret = -EBUSY;
    else
        ret = __spi_async(spi, message);  //    __spi_async

    spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);

    return ret;
}

static int __spi_async(struct spi_device *spi, struct spi_message *message)
{
    struct spi_master *master = spi->master;   //  master

    message->spi = spi;  //spi_message      

    trace_spi_message_submit(message);

    return master->transfer(spi, message);  //  master->transfer    
}

transfer 함수 의 실현 은 컨트롤 러 구동 장 에서 상세 한 설명 이 있 으 며,이 곳 에 서 는 중복 되 지 않 습 니 다.

좋은 웹페이지 즐겨찾기