cregit-Linux how code gets into the kernel

Release 4.11 drivers/char/snsc.c

Directory: drivers/char
/*
 * SN Platform system controller communication support
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) 2004, 2006 Silicon Graphics, Inc. All rights reserved.
 */

/*
 * System controller communication driver
 *
 * This driver allows a user process to communicate with the system
 * controller (a.k.a. "IRouter") network in an SGI SN system.
 */

#include <linux/interrupt.h>
#include <linux/sched/signal.h>
#include <linux/device.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <asm/sn/io.h>
#include <asm/sn/sn_sal.h>
#include <asm/sn/module.h>
#include <asm/sn/geo.h>
#include <asm/sn/nodepda.h>
#include "snsc.h"


#define SYSCTL_BASENAME	"snsc"


#define SCDRV_BUFSZ	2048

#define SCDRV_TIMEOUT	1000

static DEFINE_MUTEX(scdrv_mutex);

static irqreturn_t scdrv_interrupt(int irq, void *subch_data) { struct subch_data_s *sd = subch_data; unsigned long flags; int status; spin_lock_irqsave(&sd->sd_rlock, flags); spin_lock(&sd->sd_wlock); status = ia64_sn_irtr_intr(sd->sd_nasid, sd->sd_subch); if (status > 0) { if (status & SAL_IROUTER_INTR_RECV) { wake_up(&sd->sd_rq); } if (status & SAL_IROUTER_INTR_XMIT) { ia64_sn_irtr_intr_disable (sd->sd_nasid, sd->sd_subch, SAL_IROUTER_INTR_XMIT); wake_up(&sd->sd_wq); } } spin_unlock(&sd->sd_wlock); spin_unlock_irqrestore(&sd->sd_rlock, flags); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
Greg Howard132100.00%1100.00%
Total132100.00%1100.00%

/* * scdrv_open * * Reserve a subchannel for system controller communication. */
static int scdrv_open(struct inode *inode, struct file *file) { struct sysctl_data_s *scd; struct subch_data_s *sd; int rv; /* look up device info for this device file */ scd = container_of(inode->i_cdev, struct sysctl_data_s, scd_cdev); /* allocate memory for subchannel data */ sd = kzalloc(sizeof (struct subch_data_s), GFP_KERNEL); if (sd == NULL) { printk("%s: couldn't allocate subchannel data\n", __func__); return -ENOMEM; } /* initialize subch_data_s fields */ sd->sd_nasid = scd->scd_nasid; sd->sd_subch = ia64_sn_irtr_open(scd->scd_nasid); if (sd->sd_subch < 0) { kfree(sd); printk("%s: couldn't allocate subchannel\n", __func__); return -EBUSY; } spin_lock_init(&sd->sd_rlock); spin_lock_init(&sd->sd_wlock); init_waitqueue_head(&sd->sd_rq); init_waitqueue_head(&sd->sd_wq); sema_init(&sd->sd_rbs, 1); sema_init(&sd->sd_wbs, 1); file->private_data = sd; /* hook this subchannel up to the system controller interrupt */ mutex_lock(&scdrv_mutex); rv = request_irq(SGI_UART_VECTOR, scdrv_interrupt, IRQF_SHARED, SYSCTL_BASENAME, sd); if (rv) { ia64_sn_irtr_close(sd->sd_nasid, sd->sd_subch); kfree(sd); printk("%s: irq request failed (%d)\n", __func__, rv); mutex_unlock(&scdrv_mutex); return -EBUSY; } mutex_unlock(&scdrv_mutex); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Greg Howard23090.91%116.67%
Arnd Bergmann155.93%116.67%
Jonathan Corbet31.19%116.67%
Harvey Harrison31.19%116.67%
Jes Sorensen10.40%116.67%
Thomas Gleixner10.40%116.67%
Total253100.00%6100.00%

/* * scdrv_release * * Release a previously-reserved subchannel. */
static int scdrv_release(struct inode *inode, struct file *file) { struct subch_data_s *sd = (struct subch_data_s *) file->private_data; int rv; /* free the interrupt */ free_irq(SGI_UART_VECTOR, sd); /* ask SAL to close the subchannel */ rv = ia64_sn_irtr_close(sd->sd_nasid, sd->sd_subch); kfree(sd); return rv; }

Contributors

PersonTokensPropCommitsCommitProp
Greg Howard63100.00%1100.00%
Total63100.00%1100.00%

/* * scdrv_read * * Called to read bytes from the open IRouter pipe. * */
static inline int read_status_check(struct subch_data_s *sd, int *len) { return ia64_sn_irtr_recv(sd->sd_nasid, sd->sd_subch, sd->sd_rb, len); }

Contributors

PersonTokensPropCommitsCommitProp
Greg Howard34100.00%1100.00%
Total34100.00%1100.00%


static ssize_t scdrv_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos) { int status; int len; unsigned long flags; struct subch_data_s *sd = (struct subch_data_s *) file->private_data; /* try to get control of the read buffer */ if (down_trylock(&sd->sd_rbs)) { /* somebody else has it now; * if we're non-blocking, then exit... */ if (file->f_flags & O_NONBLOCK) { return -EAGAIN; } /* ...or if we want to block, then do so here */ if (down_interruptible(&sd->sd_rbs)) { /* something went wrong with wait */ return -ERESTARTSYS; } } /* anything to read? */ len = CHUNKSIZE; spin_lock_irqsave(&sd->sd_rlock, flags); status = read_status_check(sd, &len); /* if not, and we're blocking I/O, loop */ while (status < 0) { DECLARE_WAITQUEUE(wait, current); if (file->f_flags & O_NONBLOCK) { spin_unlock_irqrestore(&sd->sd_rlock, flags); up(&sd->sd_rbs); return -EAGAIN; } len = CHUNKSIZE; set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&sd->sd_rq, &wait); spin_unlock_irqrestore(&sd->sd_rlock, flags); schedule_timeout(msecs_to_jiffies(SCDRV_TIMEOUT)); remove_wait_queue(&sd->sd_rq, &wait); if (signal_pending(current)) { /* wait was interrupted */ up(&sd->sd_rbs); return -ERESTARTSYS; } spin_lock_irqsave(&sd->sd_rlock, flags); status = read_status_check(sd, &len); } spin_unlock_irqrestore(&sd->sd_rlock, flags); if (len > 0) { /* we read something in the last read_status_check(); copy * it out to user space */ if (count < len) { pr_debug("%s: only accepting %d of %d bytes\n", __func__, (int) count, len); } len = min((int) count, len); if (copy_to_user(buf, sd->sd_rb, len)) len = -EFAULT; } /* release the read buffer and wake anyone who might be * waiting for it */ up(&sd->sd_rbs); /* return the number of characters read in */ return len; }

Contributors

PersonTokensPropCommitsCommitProp
Greg Howard33297.36%125.00%
Domen Puncer51.47%125.00%
Nicholas Mc Guire30.88%125.00%
Harvey Harrison10.29%125.00%
Total341100.00%4100.00%

/* * scdrv_write * * Writes a chunk of an IRouter packet (or other system controller data) * to the system controller. * */
static inline int write_status_check(struct subch_data_s *sd, int count) { return ia64_sn_irtr_send(sd->sd_nasid, sd->sd_subch, sd->sd_wb, count); }

Contributors

PersonTokensPropCommitsCommitProp
Greg Howard33100.00%1100.00%
Total33100.00%1100.00%


static ssize_t scdrv_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos) { unsigned long flags; int status; struct subch_data_s *sd = (struct subch_data_s *) file->private_data; /* try to get control of the write buffer */ if (down_trylock(&sd->sd_wbs)) { /* somebody else has it now; * if we're non-blocking, then exit... */ if (file->f_flags & O_NONBLOCK) { return -EAGAIN; } /* ...or if we want to block, then do so here */ if (down_interruptible(&sd->sd_wbs)) { /* something went wrong with wait */ return -ERESTARTSYS; } } count = min((int) count, CHUNKSIZE); if (copy_from_user(sd->sd_wb, buf, count)) { up(&sd->sd_wbs); return -EFAULT; } /* try to send the buffer */ spin_lock_irqsave(&sd->sd_wlock, flags); status = write_status_check(sd, count); /* if we failed, and we want to block, then loop */ while (status <= 0) { DECLARE_WAITQUEUE(wait, current); if (file->f_flags & O_NONBLOCK) { spin_unlock_irqrestore(&sd->sd_wlock, flags); up(&sd->sd_wbs); return -EAGAIN; } set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&sd->sd_wq, &wait); spin_unlock_irqrestore(&sd->sd_wlock, flags); schedule_timeout(msecs_to_jiffies(SCDRV_TIMEOUT)); remove_wait_queue(&sd->sd_wq, &wait); if (signal_pending(current)) { /* wait was interrupted */ up(&sd->sd_wbs); return -ERESTARTSYS; } spin_lock_irqsave(&sd->sd_wlock, flags); status = write_status_check(sd, count); } spin_unlock_irqrestore(&sd->sd_wlock, flags); /* release the write buffer and wake anyone who's waiting for it */ up(&sd->sd_wbs); /* return the number of characters accepted (should be the complete * "chunk" as requested) */ if ((status >= 0) && (status < count)) { pr_debug("Didn't accept the full chunk; %d of %d\n", status, (int) count); } return status; }

Contributors

PersonTokensPropCommitsCommitProp
Greg Howard32496.72%125.00%
Domen Puncer51.49%125.00%
Nicholas Mc Guire30.90%125.00%
Dan Carpenter30.90%125.00%
Total335100.00%4100.00%


static unsigned int scdrv_poll(struct file *file, struct poll_table_struct *wait) { unsigned int mask = 0; int status = 0; struct subch_data_s *sd = (struct subch_data_s *) file->private_data; unsigned long flags; poll_wait(file, &sd->sd_rq, wait); poll_wait(file, &sd->sd_wq, wait); spin_lock_irqsave(&sd->sd_rlock, flags); spin_lock(&sd->sd_wlock); status = ia64_sn_irtr_intr(sd->sd_nasid, sd->sd_subch); spin_unlock(&sd->sd_wlock); spin_unlock_irqrestore(&sd->sd_rlock, flags); if (status > 0) { if (status & SAL_IROUTER_INTR_RECV) { mask |= POLLIN | POLLRDNORM; } if (status & SAL_IROUTER_INTR_XMIT) { mask |= POLLOUT | POLLWRNORM; } } return mask; }

Contributors

PersonTokensPropCommitsCommitProp
Greg Howard158100.00%1100.00%
Total158100.00%1100.00%

static const struct file_operations scdrv_fops = { .owner = THIS_MODULE, .read = scdrv_read, .write = scdrv_write, .poll = scdrv_poll, .open = scdrv_open, .release = scdrv_release, .llseek = noop_llseek, }; static struct class *snsc_class; /* * scdrv_init * * Called at boot time to initialize the system controller communication * facility. */
int __init scdrv_init(void) { geoid_t geoid; cnodeid_t cnode; char devname[32]; char *devnamep; struct sysctl_data_s *scd; void *salbuf; dev_t first_dev, dev; nasid_t event_nasid; if (!ia64_platform_is("sn2")) return -ENODEV; event_nasid = ia64_sn_get_console_nasid(); snsc_class = class_create(THIS_MODULE, SYSCTL_BASENAME); if (IS_ERR(snsc_class)) { printk("%s: failed to allocate class\n", __func__); return PTR_ERR(snsc_class); } if (alloc_chrdev_region(&first_dev, 0, num_cnodes, SYSCTL_BASENAME) < 0) { printk("%s: failed to register SN system controller device\n", __func__); return -ENODEV; } for (cnode = 0; cnode < num_cnodes; cnode++) { geoid = cnodeid_get_geoid(cnode); devnamep = devname; format_module_id(devnamep, geo_module(geoid), MODULE_FORMAT_BRIEF); devnamep = devname + strlen(devname); sprintf(devnamep, "^%d#%d", geo_slot(geoid), geo_slab(geoid)); /* allocate sysctl device data */ scd = kzalloc(sizeof (struct sysctl_data_s), GFP_KERNEL); if (!scd) { printk("%s: failed to allocate device info" "for %s/%s\n", __func__, SYSCTL_BASENAME, devname); continue; } /* initialize sysctl device data fields */ scd->scd_nasid = cnodeid_to_nasid(cnode); if (!(salbuf = kmalloc(SCDRV_BUFSZ, GFP_KERNEL))) { printk("%s: failed to allocate driver buffer" "(%s%s)\n", __func__, SYSCTL_BASENAME, devname); kfree(scd); continue; } if (ia64_sn_irtr_init(scd->scd_nasid, salbuf, SCDRV_BUFSZ) < 0) { printk ("%s: failed to initialize SAL for" " system controller communication" " (%s/%s): outdated PROM?\n", __func__, SYSCTL_BASENAME, devname); kfree(scd); kfree(salbuf); continue; } dev = first_dev + cnode; cdev_init(&scd->scd_cdev, &scdrv_fops); if (cdev_add(&scd->scd_cdev, dev, 1)) { printk("%s: failed to register system" " controller device (%s%s)\n", __func__, SYSCTL_BASENAME, devname); kfree(scd); kfree(salbuf); continue; } device_create(snsc_class, NULL, dev, NULL, "%s", devname); ia64_sn_irtr_intr_enable(scd->scd_nasid, 0 /*ignored */ , SAL_IROUTER_INTR_RECV); /* on the console nasid, prepare to receive * system controller environmental events */ if(scd->scd_nasid == event_nasid) { scdrv_event_init(scd); } } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Greg Howard32680.89%216.67%
Zhouyi Zhou317.69%18.33%
Greg Edwards163.97%18.33%
Patrick Gefre112.73%18.33%
Andrew Morton61.49%18.33%
Greg Kroah-Hartman51.24%325.00%
Harvey Harrison51.24%18.33%
Jack Steiner20.50%18.33%
Jes Sorensen10.25%18.33%
Total403100.00%12100.00%

device_initcall(scdrv_init);

Overall Contributors

PersonTokensPropCommitsCommitProp
Greg Howard171691.76%28.33%
Zhouyi Zhou311.66%14.17%
Arnd Bergmann271.44%28.33%
Patrick Gefre170.91%14.17%
Greg Edwards160.86%14.17%
Greg Kroah-Hartman110.59%416.67%
Domen Puncer100.53%14.17%
Harvey Harrison90.48%14.17%
Nicholas Mc Guire60.32%14.17%
Andrew Morton60.32%14.17%
Jonathan Corbet50.27%14.17%
Jesse Barnes30.16%14.17%
Dan Carpenter30.16%14.17%
Jes Sorensen30.16%14.17%
Paul Gortmaker20.11%14.17%
Jack Steiner20.11%14.17%
Thomas Gleixner10.05%14.17%
Arjan van de Ven10.05%14.17%
Ingo Molnar10.05%14.17%
Total1870100.00%24100.00%
Directory: drivers/char
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.