Pni11096 spi.c

This is drivers/spi/pni11096_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 


 * 1) define DEV_NAME "pni11096-spi"
 * 2) define DEV_NAME_LONG "PNI-11096 3-axis magneto-inductive sensor"
 * 3) define DRV_VERSION_STR "version 01.00"

MODULE_AUTHOR("Andrei Rylin "); MODULE_DESCRIPTION(DEV_NAME_LONG); MODULE_LICENSE("GPL");


 * 1) define CMD_DHST 0x80 /* high speed oscillator test */
 * 2) define CMD_PS2 0x40 /* period select */
 * 3) define CMD_PS1 0x20
 * 4) define CMD_PS0 0x10
 * 5) define CMD_ODIR 0x08 /* oscillator direction */
 * 6) define CMD_MOT 0x04 /* magnetic oscillator test */
 * 7) define CMD_AS1 0x02 /* axis select */
 * 8) define CMD_AS0 0x01


 * 1) define CMD_PS_MASK 0x70
 * 2) define CMD_AS_MASK 0x03
 * 3) define CMD_AS_2MHZ 0
 * 4) define CMD_AS_X   1
 * 5) define CMD_AS_Y   2
 * 6) define CMD_AS_Z   3


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


 * 1) define PROC_MODE   0x00
 * 2) define PROC_STAT   0x01
 * 3) define PROC_PERF   0x02
 * 4) define PROC_PERF1  0x03
 * 5) define PROC_PERIOD 0x04
 * 6) define PROC_WHOAMI 0x05


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

struct this_proc_entry { char id; const char* name; };

struct this_data { struct this_dev* dev; struct list_head link; u16 buf[3]; };

struct cmd_msg { struct this_dev* dev; struct spi_message msg; struct spi_transfer xfer; int axis; ALIGNED(8) u8 command; };

struct dat_msg { struct this_dev* dev; struct spi_message msg; struct spi_transfer xfer; int axis; ALIGNED(8) u16 data; };

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

struct this_dev { struct list_head link; int minor; int flags; int shift; /* pre-calculated period scaling */ struct class_device *class_dev; struct spi_device* spi;

spinlock_t lock; struct this_data data_buf[N_BUFS]; struct list_head free_data; struct list_head pending_data;

struct list_head clients; wait_queue_head_t waitq;

struct cmd_msg cmd[3]; struct list_head free_cmd;

struct dat_msg dat[3]; struct list_head free_dat;

unsigned long samples; unsigned long overruns;

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\n", "%#.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 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; 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; spin_unlock_irqrestore(&dev->lock, flags); p += sprintf(p, "%ld samples %ld overruns\n",           samples, overruns); break; case PROC_PERIOD: spin_lock_irqsave(&dev->lock, flags); i = 7 - dev->shift; spin_unlock_irqrestore(&dev->lock, flags); p += sprintf(p, "%d\n", i); break; case PROC_PERF: case PROC_PERF1: spin_lock_irqsave(&dev->lock, flags); samples = dev->samples; overruns = dev->overruns; 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; spin_unlock_irqrestore(&dev->lock, flags); p += sprintf(p, "%ld samples %ld overruns per second\n",           samples, overruns); 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; char c, lbuf[16], *p = lbuf; unsigned long flags;

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

if (count>15) return -EINVAL;

if (copy_from_user(lbuf, buf, count)) return -EFAULT; lbuf[count] = 0;

while(*p && (isspace(*p) || ispunct(*p))) p++;

switch (data->entry->id) { case PROC_MODE: 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: spin_unlock_irqrestore(&dev->lock, flags); dev_info(&dev->spi->dev, "expect [dec|hex|bin]\n"); return -EINVAL; }       spin_unlock_irqrestore(&dev->lock, flags); break; case PROC_PERIOD: c = simple_strtoul(p, NULL, 0) & 7; spin_lock_irqsave(&dev->lock, flags); dev->shift = 7 - c;       c <<= 4; {           int i;            for(i=0;i<3;i++) { dev->cmd[i].command &= ~CMD_PS_MASK; dev->cmd[i].command |= c;           } }       spin_unlock_irqrestore(&dev->lock, flags); break; case PROC_STAT: spin_lock_irqsave(&dev->lock, flags); dev->samples = 0; dev->overruns = 0; spin_unlock_irqrestore(&dev->lock, flags); break; default: return -EINVAL; }   return count; }

static inline void __this_start(struct this_dev *dev) {   struct cmd_msg* cmd; BUG_ON(list_empty(&dev->free_cmd)); cmd = list_entry(dev->free_cmd.next, struct cmd_msg, msg.queue); list_del_init(&cmd->msg.queue); spi_async(dev->spi, &cmd->msg); }

static inline void this_start(struct this_dev *dev) {   unsigned long flags; spin_lock_irqsave(&dev->lock, flags); __this_start(dev); spin_unlock_irqrestore(&dev->lock, flags); }

void this_cmd_complete(void *context) {   unsigned long flags; struct cmd_msg* cmd = context; struct this_dev* dev = cmd->dev;

spin_lock_irqsave(&dev->lock, flags); list_add_tail(&cmd->msg.queue, &dev->free_cmd); spin_unlock_irqrestore(&dev->lock, flags); }

void this_data_complete(void *context) {   unsigned long flags; struct dat_msg* dat = context; struct this_dev* dev = dat->dev; struct this_data* data; struct cmd_msg* cmd;

spin_lock_irqsave(&dev->lock, flags); if (list_empty(&dev->free_data)) { BUG_ON(list_empty(&dev->pending_data)); dev->overruns++; list_move_tail(dev->pending_data.next, &dev->free_data); }   data = list_entry(dev->free_data.next, struct this_data, link); data->buf[dat->axis] = ntohs(dat->data) << dev->shift;

list_add_tail(&dat->msg.queue, &dev->free_dat); if (dat->axis == 2) { if (list_empty(&dev->clients)) { spin_unlock_irqrestore(&dev->lock, flags); return; }       list_move_tail(dev->free_data.next, &dev->pending_data); wake_up_interruptible(&dev->waitq); }

BUG_ON(list_empty(&dev->free_cmd)); cmd = list_entry(dev->free_cmd.next, struct cmd_msg, msg.queue); list_del_init(&cmd->msg.queue); spin_unlock_irqrestore(&dev->lock, flags); spi_async(dev->spi, &cmd->msg); }

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; }


 * 1) define DELAY_uSECS 0

static void this_data_init(struct this_data* data, struct this_dev* dev) {   data->dev = dev; list_add_tail(&data->link, &dev->free_data); }

static void cmd_init(struct cmd_msg* cmd, struct this_dev* dev, int axis) {   cmd->dev = dev; cmd->axis = axis; cmd->command = axis + 1;

spi_message_init(&cmd->msg);

cmd->msg.complete = this_cmd_complete; cmd->msg.context = cmd; cmd->msg.is_dma_mapped = 1;

cmd->xfer.len = 1; cmd->xfer.tx_buf = &cmd->command; BUG_ON(!IS_DMA_ALIGNED(&cmd->command)); cmd->xfer.cs_change = 1; cmd->xfer.cs_reset = 1; cmd->xfer.delay_usecs = DELAY_uSECS; cmd->xfer.tx_dma = dma_map_single(&dev->spi->dev,       (void*)cmd->xfer.tx_buf, 1, DMA_FROM_DEVICE);

spi_message_add_tail(&cmd->xfer, &cmd->msg); list_add_tail(&cmd->msg.queue, &dev->free_cmd); }

static void dat_init(struct dat_msg* dat, struct this_dev* dev, int axis) {   dat->dev = dev; dat->axis = axis;

spi_message_init(&dat->msg);

dat->msg.complete = this_data_complete; dat->msg.context = dat; dat->msg.is_dma_mapped = 1;

dat->xfer.len = 2; dat->xfer.rx_buf = &dat->data; BUG_ON(!IS_DMA_ALIGNED(&dat->data)); dat->xfer.cs_change = 1; dat->xfer.delay_usecs = DELAY_uSECS; dat->xfer.rx_dma = dma_map_single(&dev->spi->dev,       (void*)dat->xfer.rx_buf, 2, DMA_TO_DEVICE);

spi_message_add_tail(&dat->xfer, &dat->msg); list_add_tail(&dat->msg.queue, &dev->free_dat); }

static inline void cmd_deinit(struct cmd_msg* cmd) {   dma_unmap_single(&cmd->dev->spi->dev, cmd->xfer.tx_dma,        1, DMA_FROM_DEVICE); }

static inline void dat_deinit(struct dat_msg* dat) {   dma_unmap_single(&dat->dev->spi->dev, dat->xfer.rx_dma,        2, DMA_TO_DEVICE); }

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);

INIT_LIST_HEAD(&dev->free_cmd); INIT_LIST_HEAD(&dev->free_dat);

for (i=0; i<3; i++) { cmd_init(&dev->cmd[i], dev, i); dat_init(&dev->dat[i], dev, i); }

for (i=0; idata_buf[i], dev);

/* dev->flags = 0: default mode dec, period 0 */ dev->shift = 7;

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 { for (i=0; i<3; i++) { cmd_deinit(&dev->cmd[i]); dat_deinit(&dev->dat[i]); }       kfree(dev); dev = NULL; }   spin_unlock(&this_dev_list_lock); return dev; }

static inline void return_this_dev(struct this_dev* dev) {   int i;    spin_lock(&this_dev_list_lock); list_del(&dev->link); spin_unlock(&this_dev_list_lock); for (i=0; i<3; i++) { cmd_deinit(&dev->cmd[i]); dat_deinit(&dev->dat[i]); } }

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; u16 d[3];

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; }   data = list_entry(dev->pending_data.next, struct this_data, link); memcpy(d, data->buf, 6); list_move_tail(dev->pending_data.next, &dev->free_data); dev->samples++; spin_unlock_irqrestore(&dev->lock, flags); if (dev->flags & FLAGS_MODE_BIN) { copy_to_user(buf, d, 6); ret = 6; } else { ret = sprintf(buf, dev_mode_fmt[dev->flags & FLAGS_MODE_HEX],           d[0], d[1], d[2]); }   *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_open(struct inode *inode, struct file *file) {   struct this_dev* dev; struct this_client* client; 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(&dev->lock); if (list_empty(&dev->clients)) first = 1; list_add_tail(&client->link, &dev->clients); if (first) this_start(dev); spin_unlock(&dev->lock);

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;

spin_lock(&dev->lock); list_del(&client->link); spin_unlock(&dev->lock);

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 dat_msg* dat;

spin_lock(&dev->lock); BUG_ON(list_empty(&dev->free_dat)); dat = list_entry(dev->free_dat.next, struct dat_msg, msg.queue); list_del_init(&dat->msg.queue); BUG_ON(dat->msg.status); spin_unlock(&dev->lock);

spi_async(dev->spi, &dat->msg); return IRQ_HANDLED; }

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

i = spi_setup(spi); if (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; iproc_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) {   int i;    struct this_dev* dev = get_this_dev_by_spi(spi); if (!dev) return -ENODEV; free_irq(spi->irq, dev); spin_lock(&dev->lock); if (!list_empty(&dev->clients)) { spin_unlock(&dev->lock); return -EBUSY; }   return_this_dev(dev); spin_unlock(&dev->lock);

BUG_ON(!dev->proc_parent); 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, };

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 = 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: 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); } module_exit(this_exit);