Robostix spi.c

This is drivers/spi/robostix_spi.c /* * Copyright (C) 2006 Andrei Rylin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */


 * 1) include 
 * 2) include 
 * 3) include 
 * 4) include 
 * 5) include 
 * 6) include 
 * 7) include 
 * 8) include 
 * 9) include 
 * 10) include 
 * 11) include 
 * 12) include 
 * 13) include 
 * 14) include 
 * 15) include 
 * 16) include 
 * 17) include <asm/delay.h>


 * 1) define DEV_NAME "robostix-spi"
 * 2) define DEV_NAME_LONG "Robostix as SPI slave"
 * 3) define DRV_VERSION_STR "version 01.00"

MODULE_AUTHOR("Andrei Rylin <port777@gmail.com>"); MODULE_DESCRIPTION(DEV_NAME_LONG); MODULE_LICENSE("GPL");


 * 1) define ALIGNED(x) __attribute__((aligned(x)))
 * 2) define IS_DMA_ALIGNED(x) (((u32)(x)&7)==0)
 * 3) define N_BUFS 2


 * 1) define SPI_SYN 0x69
 * 2) define SPI_NULL 0xcc


 * 1) define FLAGS_MODE_HEX 0x01
 * 2) define FLAGS_MODE_BIN 0x02

/* microseconds */
 * 1) define BYTE_DELAY 25
 * 2) define COMMAND_DELAY 100


 * 1) define MAX_MESSAGE_LENGTH 30

struct this_proc_entry { char id; const char* name; };
 * 1) define PROC_MODE  0x01
 * 2) define PROC_STAT  0x02
 * 3) define PROC_PERF  0x03
 * 4) define PROC_PERF1 0x04
 * 5) define PROC_WHOAMI 0x05

static const struct this_proc_entry this_proc_entries[] = { {PROC_MODE,  "mode"}, {PROC_STAT,  "stat"}, {PROC_PERF,  "perf"}, {PROC_PERF1, "perf1"}, {PROC_WHOAMI, "whoami"}, };

struct this_data { struct this_dev* dev; struct spi_message message; struct spi_transfer t[22]; u8 buf[19]; /* dummy + length + 8 x 16-bit ADC data + crc */ };

struct this_dev { struct list_head link; int minor; int flags; struct class_device *class_dev; struct spi_device* spi;

spinlock_t lock;

struct list_head clients; wait_queue_head_t waitq;

struct this_data* data; struct list_head free_data; struct list_head pending_data;

u8 read_adc_cmd[3];

unsigned long samples; unsigned long overruns; unsigned long errors;

struct proc_dir_entry *proc_parent; struct proc_dir_entry *proc_entry[ARRAY_SIZE(this_proc_entries)]; };

struct this_proc_data { const struct this_proc_entry* entry; struct this_dev* dev; };

struct this_client { struct list_head link; struct this_dev* dev; };

static const char* dev_mode[] = { "dec", "hex", "bin", "bin" }; static const char* dev_mode_fmt[] = { "%hd %hd %hd %hd %hd %hd %hd %hd\n", "%#.4hx %#.4hx %#.4hx %#.4hx %#.4hx %#.4hx %#.4hx %#.4hx\n", };

static struct class* this_dev_class; static int this_major; static LIST_HEAD(this_dev_list); static DEFINE_SPINLOCK(this_dev_list_lock);

static inline u8 __crc8(u8 crc, u8 data) {   int i;    crc ^= data; for (i=0; i<8; i++) { if (crc & 0x80) { crc <<= 1; crc ^= 0x07; } else { crc <<= 1; }   }    return crc; }

static inline u8 do_crc8(u8 crc, const u8* buf, size_t n) { while(n--) crc = __crc8(crc, *buf++); return crc; }

static struct spi_message* spi_msg;

/* * Insert a delay between each byte. */ static int this_io(struct this_dev *dev, const u8 *tx, u8 *rx, size_t n) { static DECLARE_MUTEX(lock); struct spi_message *m; struct spi_transfer *t; int ret;

BUG_ON(tx == NULL && rx == NULL); if (n > MAX_MESSAGE_LENGTH+2) return -EINVAL;

ret = sizeof(*m) + n * sizeof(*t); if (down_trylock(&lock)) { m = kmalloc(ret, GFP_KERNEL); if (!m) return -ENOMEM; } else m = spi_msg; memset(m, 0, ret);

INIT_LIST_HEAD(&m->transfers); t = (struct spi_transfer*)(m+1); while(n--) { t->rx_buf     = rx; t->tx_buf     = tx; t->len        = 1; t->delay_usecs = BYTE_DELAY; spi_message_add_tail(t++, m); if (tx) tx++; if (rx) rx++; }   ret = spi_sync(dev->spi, m); if (m == spi_msg) up(&lock); else kfree(m); return ret; }

static inline int this_recover(struct this_dev* dev) {   u8 rx, tx = SPI_SYN; int i;   for (i=0;i<34;i++) this_io(dev, &tx, 0, 1); return this_io(dev, &tx, &rx, 1) || rx != SPI_NULL; }


 * 1) define SPI_CMD_START_ADC 0x70
 * 2) define SPI_CMD_STOP_ADC 0x71
 * 3) define SPI_CMD_READ_ADC 0x72

static int this_start_adc(struct this_dev* dev) {   u8 tx[3] = { 1, /* 1 byte follows - command */ SPI_CMD_START_ADC, 0, /* crc - tbd */ };   tx[2] = do_crc8(0xff, tx, 2); return this_io(dev, tx, 0, 3); }

static int this_stop_adc(struct this_dev* dev) {   u8 tx[3] = { 1, /* 1 byte follows - command */ SPI_CMD_STOP_ADC, 0, /* crc - tbd */ };   tx[2] = do_crc8(0xff, tx, 2); return this_io(dev, tx, 0, 3); }

static int this_read_proc(char *p, char **start, off_t off,   int count, int *eof, void *_data) {   struct this_proc_data* data = _data; struct this_dev* dev = data->dev; unsigned long flags, samples, overruns, errors; int i;

*eof = 1; *start = p;

if (off && data->entry->id != PROC_PERF) return 0;

switch (data->entry->id) { case PROC_WHOAMI: p += sprintf(p, "device: " DEV_NAME_LONG "\n"); p += sprintf(p, "driver: " DRV_VERSION_STR "\n"); break; case PROC_MODE: spin_lock_irqsave(&dev->lock, flags); i = dev->flags & 3; spin_unlock_irqrestore(&dev->lock, flags); p += sprintf(p, "%s\n", dev_mode[i]); break; case PROC_STAT: spin_lock_irqsave(&dev->lock, flags); samples = dev->samples; overruns = dev->overruns; errors = dev->errors; spin_unlock_irqrestore(&dev->lock, flags); p += sprintf(p, "%ld samples %ld overruns %ld errors\n",           samples, overruns, errors); break; case PROC_PERF: case PROC_PERF1: spin_lock_irqsave(&dev->lock, flags); samples = dev->samples; overruns = dev->overruns; errors = dev->errors; spin_unlock_irqrestore(&dev->lock, flags); flags = schedule_timeout_interruptible(msecs_to_jiffies(1000)); if (flags) return flags; spin_lock_irqsave(&dev->lock, flags); samples = dev->samples - samples; overruns = dev->overruns - overruns; errors = dev->errors - errors; spin_unlock_irqrestore(&dev->lock, flags); p += sprintf(p, "%ld samples %ld overruns %ld errors per second\n",           samples, overruns, errors); break; default: return -EINVAL; }   return p-*start; }

static int this_write_proc(struct file *file, const char __user *buf,   unsigned long count, void *_data) {   struct this_proc_data* data = _data; struct this_dev* dev = data->dev; unsigned long flags; char lbuf[16], *p;

if (!capable(CAP_SYS_ADMIN)) return -EACCES;

switch (data->entry->id) { case PROC_MODE: if (count > 15) return -EINVAL; if (copy_from_user(lbuf, buf, count)) return -EFAULT; lbuf[count] = 0; for(p=lbuf; *p && !isalpha(*p); p++) ; spin_lock_irqsave(&dev->lock, flags); switch (tolower(*p)) { case 'b': dev->flags |= FLAGS_MODE_BIN; break; case 'h': dev->flags &= ~FLAGS_MODE_BIN; dev->flags |= FLAGS_MODE_HEX; break; case 'd': dev->flags &= ~(FLAGS_MODE_BIN|FLAGS_MODE_HEX); break; default: dev_info(&dev->spi->dev, "expect [dec|hex|bin]\n"); count = -EINVAL; }       spin_unlock_irqrestore(&dev->lock, flags); break; case PROC_STAT: spin_lock_irqsave(&dev->lock, flags); dev->samples = 0; dev->overruns = 0; dev->errors = 0; spin_unlock_irqrestore(&dev->lock, flags); break; default: count = -EINVAL; }   return count; }

void this_data_ready(void *context) {   unsigned long flags; struct this_data* data = context; struct this_dev* dev = data->dev;

spin_lock_irqsave(&dev->lock, flags); list_add_tail(&data->message.queue, &dev->pending_data); wake_up_interruptible(&dev->waitq); spin_unlock_irqrestore(&dev->lock, flags); }

static inline struct this_dev* __get_this_dev_by_minor(int minor) {   struct this_dev* dev; list_for_each_entry(dev, &this_dev_list, link) if (dev->minor == minor) return dev; return NULL; }

static inline struct this_dev* get_this_dev_by_minor(int minor) {   struct this_dev* dev; spin_lock(&this_dev_list_lock); dev = __get_this_dev_by_minor(minor); spin_unlock(&this_dev_list_lock); return dev; }

static void this_data_init(struct this_data* data, struct this_dev* dev) {   int i;    struct spi_transfer *t = data->t; spi_message_init(&data->message); data->message.complete = this_data_ready; data->message.context = data; data->dev = dev;

for (i=0;i<3;i++) { t->tx_buf = dev->read_adc_cmd + i;       t->len = 1; if (i == 2) t->delay_usecs = COMMAND_DELAY; else t->delay_usecs = BYTE_DELAY; spi_message_add_tail(t++, &data->message); }

for (i=0;i<19;i++) { t->rx_buf     = data->buf + i,        t->len         = 1, t->delay_usecs = BYTE_DELAY; spi_message_add_tail(t++, &data->message); }

list_add_tail(&data->message.queue, &dev->free_data); }

static struct this_dev* new_this_dev(struct spi_device* spi) {   int i;    struct this_dev* dev; dev = kzalloc(sizeof *dev, GFP_KERNEL); if (!dev) return ERR_PTR(-ENOMEM);

dev->spi = spi;

spin_lock_init(&dev->lock); INIT_LIST_HEAD(&dev->free_data); INIT_LIST_HEAD(&dev->pending_data); dev->data = kzalloc(N_BUFS * sizeof(struct this_data), GFP_KERNEL); if (!dev->data) { kfree(dev); return ERR_PTR(-ENOMEM); }

dev->read_adc_cmd[0] = 1; dev->read_adc_cmd[1] = SPI_CMD_READ_ADC; dev->read_adc_cmd[2] = do_crc8(0xff, dev->read_adc_cmd, 2);

for (i=0; i<N_BUFS; i++) this_data_init(&dev->data[i], dev);

INIT_LIST_HEAD(&dev->clients); init_waitqueue_head(&dev->waitq);

spin_lock(&this_dev_list_lock); for(dev->minor = 0; dev->minor < 256; dev->minor++) if(!__get_this_dev_by_minor(dev->minor)) break; if (dev->minor < 256) list_add_tail(&dev->link, &this_dev_list); else { kfree(dev); dev = NULL; }   spin_unlock(&this_dev_list_lock); return dev; }

static inline void return_this_dev(struct this_dev* dev) {   spin_lock(&this_dev_list_lock); list_del(&dev->link); spin_unlock(&this_dev_list_lock); }

static inline struct this_dev* get_this_dev_by_spi(   struct spi_device *spi) {   struct this_dev* dev; spin_lock(&this_dev_list_lock); list_for_each_entry(dev, &this_dev_list, link) { if (dev->spi == spi) { spin_unlock(&this_dev_list_lock); return dev; }   }    spin_unlock(&this_dev_list_lock); return NULL; }

static ssize_t show_dev_name(struct class_device *class_dev, char *buf) {   struct this_dev* dev = get_this_dev_by_minor(MINOR(class_dev->devt)); if (!dev) return -ENODEV; return sprintf(buf, DEV_NAME "%d.%d\n",       dev->spi->master->bus_num, dev->spi->chip_select); }  static CLASS_DEVICE_ATTR(name, S_IRUGO, show_dev_name, NULL);

static ssize_t this_read(struct file *file, char __user *buf,    size_t count, loff_t *offset) {   int ret; unsigned long flags; struct this_client *client = file->private_data; struct this_dev *dev = client->dev; struct this_data* data; struct list_head* elm; u8 d[19]; /* pad + length + 16 bytes data + crc8 */

again: if (list_empty(&dev->pending_data) && file->f_flags & O_NONBLOCK) return -EAGAIN;

ret = wait_event_interruptible(dev->waitq,                                   !list_empty(&dev->pending_data)); if (ret) return ret;

spin_lock_irqsave(&dev->lock, flags); if (list_empty(&dev->pending_data)) { spin_unlock_irqrestore(&dev->lock, flags); goto again; }   elm = dev->pending_data.next; list_del_init(elm); data = list_entry(elm, struct this_data, message.queue); memcpy(d, data->buf, 19); list_add_tail(elm, &dev->free_data); spin_unlock_irqrestore(&dev->lock, flags);

if (do_crc8(0xff, d+1, 18)) { dev->errors++; dev_info(&dev->spi->dev, "crc error\n"); for(ret=0;ret<20;ret++) printk("%02x ", d[ret]); printk("\n"); this_recover(dev); goto again; }
 * 1) ifdef DEBUG
 * 1) endif

dev->samples++; if (dev->flags & FLAGS_MODE_BIN) { copy_to_user(buf, d+2, 16); ret = 16; } else { ret = sprintf(buf, dev_mode_fmt[dev->flags & FLAGS_MODE_HEX],           *(u16*)(d+ 2), *(u16*)(d+ 4), *(u16*)(d+ 6), *(u16*)(d+ 8),            *(u16*)(d+10), *(u16*)(d+12), *(u16*)(d+14), *(u16*)(d+16)); }   *offset = 0; BUG_ON(ret > count); return ret; }

static unsigned int this_poll(struct file *file, poll_table *wait) {   struct this_client *client = file->private_data; struct this_dev *dev = client->dev;

poll_wait(file, &dev->waitq, wait);

if (!list_empty(&dev->pending_data)) return POLLIN | POLLRDNORM;

return 0; }

static int this_ioctl(struct inode *inode, struct file *file,   unsigned int cmd, unsigned long arg) {   static DECLARE_MUTEX(lock); struct this_client *client = file->private_data; struct this_dev *dev = client->dev;

struct i2c_rdwr_ioctl_data rdwr_arg; struct i2c_msg msg;

u8 io[MAX_MESSAGE_LENGTH+2], crc = 0; int res = 0, i;

switch (cmd) { case I2C_SLAVE: return 0; case I2C_RDWR: if (copy_from_user(&rdwr_arg, (void*)arg, sizeof rdwr_arg)) return -EFAULT; if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS) return -EINVAL;

down(&lock); //this_stop_adc(dev); disable_irq(dev->spi->irq);

for(i=0; i<rdwr_arg.nmsgs; i++) {           if (copy_from_user(&msg, rdwr_arg.msgs + i, sizeof msg)) { res = -EFAULT; break; }           if (msg.len > MAX_MESSAGE_LENGTH) { res = -EINVAL; break; }           if (msg.flags & I2C_M_RD) { res = this_io(dev, 0, io, msg.len+2); if (res) break; if (do_crc8(0xff, io+1, msg.len+1)) { int j;                   dev_info(&dev->spi->dev, "read crc error\n"); for (j=0;j<msg.len+1;j++) printk("0x%02x ", io[j]); printk("\n"); this_recover(dev); res = -EIO; break; }               crc = __crc8(crc, (msg.addr << 1) | 1); crc = do_crc8(crc, io+2, msg.len-1); io[msg.len+1] = crc; if (copy_to_user(msg.buf, io+2, msg.len)) { res = -EFAULT; break; }           } else { if (copy_from_user(io+1, msg.buf, msg.len)) { res = -EFAULT; break; }               io[0] = msg.len; io[msg.len+1] = do_crc8(0xff, io, msg.len+1); res = this_io(dev, io, 0, msg.len+2); if (res) break; crc = __crc8(0, msg.addr << 1); crc = do_crc8(crc, io+1, msg.len); }           udelay(COMMAND_DELAY); }       enable_irq(dev->spi->irq); //this_start_adc(dev); up(&lock); return res;
 * 1) ifdef DEBUG
 * 1) endif

default: res = -EINVAL; break; }   return res; }

static int this_open(struct inode *inode, struct file *file) {   struct this_dev* dev; struct this_client* client; unsigned long flags; int first = 0;

dev = get_this_dev_by_minor(iminor(inode)); if (!dev) return -ENODEV; client = kzalloc(sizeof(*client), GFP_KERNEL); if (!client) return -ENOMEM; client->dev = dev; file->private_data = client; INIT_LIST_HEAD(&client->link);

spin_lock_irqsave(&dev->lock, flags); if (list_empty(&dev->clients)) first = 1; list_add_tail(&client->link, &dev->clients); spin_unlock_irqrestore(&dev->lock, flags); if (first) this_start_adc(dev);

return 0; }

static int this_release(struct inode *inode, struct file *file) {   struct this_client *client = file->private_data; struct this_dev *dev = client->dev; unsigned long flags; int last = 0;

spin_lock_irqsave(&dev->lock, flags); list_del(&client->link); if (list_empty(&dev->clients)) last = 1; spin_unlock_irqrestore(&dev->lock, flags); if (last) this_stop_adc(dev);

kfree(client); file->private_data = NULL; return 0; }

static irqreturn_t this_interrupt(int irq, void* context, struct pt_regs* regs) {   struct this_dev* dev = context; struct this_data* data; struct list_head* elm;

spin_lock(dev->lock); if (list_empty(&dev->free_data)) { dev->overruns++; BUG_ON(list_empty(&dev->pending_data)); elm = dev->pending_data.next; } else elm = dev->free_data.next; list_del_init(elm); spin_unlock(dev->lock);

data = list_entry(elm, struct this_data, message.queue); BUG_ON(data->message.status); if (spi_async(dev->spi, &data->message)) dev_err(&dev->spi->dev, "spi_async failed\n");

return IRQ_HANDLED; }

static int this_probe(struct spi_device *spi) {   struct this_dev* dev; int i;

spi->max_speed_hz = 3686400/5; i = spi_setup(spi); if (i) { dev_err(&spi->dev, "spi_setup failed with status %d\n", i); return i;   }

dev = new_this_dev(spi); if (IS_ERR(dev)) return PTR_ERR(dev);

dev->class_dev = class_device_create(this_dev_class, NULL,       MKDEV(this_major, dev->minor), &spi->dev,        DEV_NAME "%d.%d", spi->master->bus_num, spi->chip_select); if (!dev->class_dev) goto error;

class_device_create_file(dev->class_dev, &class_device_attr_name);

i = request_irq(spi->irq, this_interrupt, IRQF_TRIGGER_RISING,                   dev->class_dev->class_id, dev); if (i < 0) goto error_class_destroy;

dev->proc_parent = create_proc_entry(dev->class_dev->class_id,       S_IFDIR | S_IRUGO | S_IXUGO, NULL); if (!dev->proc_parent) { dev_err(&spi->dev, "failed to create /proc/%s\n",           dev->class_dev->class_id); goto error_free_irq; }   for(i=0; i<ARRAY_SIZE(this_proc_entries); i++) { dev->proc_entry[i] = create_proc_entry(            this_proc_entries[i].name, 0644, dev->proc_parent); if (dev->proc_entry[i]) { struct this_proc_data* data = kmalloc(sizeof *data, GFP_KERNEL); if (data) { data->entry = &this_proc_entries[i]; data->dev = dev; }           dev->proc_entry[i]->data = data; dev->proc_entry[i]->read_proc = this_read_proc; dev->proc_entry[i]->write_proc = this_write_proc; } else { dev_err(&spi->dev, "failed to create /proc/%s/%s\n",               dev->class_dev->class_id, this_proc_entries[i].name); }   }

return 0;

error_free_irq: free_irq(spi->irq, dev); error_class_destroy: class_device_destroy(this_dev_class, MKDEV(this_major, dev->minor)); error: return_this_dev(dev); kfree(dev); return -ENODEV; }

static void this_shutdown(struct spi_device* spi) {   dev_dbg(&spi->dev, "shutdown\n"); }

static int this_suspend(struct spi_device* spi, pm_message_t state) {   struct this_dev* dev = get_this_dev_by_spi(spi); switch (state.event) { case PM_EVENT_ON: dev_dbg(&spi->dev, "power on\n"); break; case PM_EVENT_FREEZE: dev_dbg(&spi->dev, "power freeze\n"); break; case PM_EVENT_SUSPEND: dev_dbg(&spi->dev, "power suspend\n"); break; default: dev_dbg(&spi->dev, "power %d\n", state.event); BUG_ON(1); }   return 0; }
 * 1) ifdef CONFIG_PM
 * 2) warning TODO all power management

static int this_resume(struct spi_device *spi) {   dev_dbg(&spi->dev, "power resume\n"); return this_suspend(spi, PMSG_ON); }


 * 1) else
 * 2) define this_suspend NULL
 * 3) define this_resume NULL
 * 4) endif

int this_remove(struct spi_device* spi) {   unsigned long flags; int i;   struct this_dev* dev = get_this_dev_by_spi(spi); if (!dev) return -ENODEV; spin_lock_irqsave(&dev->lock, flags); if (!list_empty(&dev->clients)) { spin_unlock_irqrestore(&dev->lock, flags); return -EBUSY; }   free_irq(spi->irq, dev); return_this_dev(dev); spin_unlock_irqrestore(&dev->lock, flags);

for(i=0; i < ARRAY_SIZE(this_proc_entries); i++) { if (dev->proc_entry[i]) { kfree(dev->proc_entry[i]->data); remove_proc_entry(this_proc_entries[i].name, dev->proc_parent); }   }    remove_proc_entry(dev->class_dev->class_id, NULL);

class_device_destroy(this_dev_class, MKDEV(this_major, dev->minor)); kfree(dev); return 0; }

static struct file_operations this_fops = { .owner     = THIS_MODULE, .llseek    = no_llseek, .read      = this_read, .open      = this_open, .release   = this_release, .poll      = this_poll, .ioctl     = this_ioctl, };

static struct spi_driver this_driver = { .driver = { .name = DEV_NAME, .bus = &spi_bus_type, .owner = THIS_MODULE, },    .probe = this_probe, .remove = __devexit_p(this_remove), .shutdown = this_shutdown, .suspend = this_suspend, .resume = this_resume, };

static int __init this_init(void) {   int res; res = sizeof(struct spi_message) + (MAX_MESSAGE_LENGTH+2) * sizeof(struct spi_transfer); spi_msg = kmalloc(res, GFP_KERNEL); if (!spi_msg) return -ENOMEM; res = register_chrdev(0, DEV_NAME, &this_fops); if (IS_ERR_VALUE(res)) goto out; this_major = res; this_dev_class = class_create(THIS_MODULE, DEV_NAME "-dev"); if (IS_ERR(this_dev_class)) goto out_unreg_chrdev; res = spi_register_driver(&this_driver); if (res) goto out_unreg_class; return 0;

out_unreg_class: class_destroy(this_dev_class); out_unreg_chrdev: unregister_chrdev(this_major, DEV_NAME); out: kfree(spi_msg); printk(KERN_ERR DEV_NAME ": driver init failed\n"); return res; } module_init(this_init);

static void __exit this_exit(void) {   spi_unregister_driver(&this_driver); class_destroy(this_dev_class); unregister_chrdev(this_major, DEV_NAME); kfree(spi_msg); } module_exit(this_exit);