This adds the core for CAN transceivers

Signed-off-by: Kurt Van Dijck <[email protected]>
----
Index: include/socketcan/can/dev.h
===================================================================
--- include/socketcan/can/dev.h (revision 1069)
+++ include/socketcan/can/dev.h (working copy)
@@ -55,6 +55,7 @@
 
   unsigned int echo_skb_max;
   struct sk_buff **echo_skb;
+       struct can_transceiver *cantr;
 };
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
Index: include/socketcan/can/error.h
===================================================================
--- include/socketcan/can/error.h       (revision 1069)
+++ include/socketcan/can/error.h       (working copy)
@@ -28,6 +28,7 @@
 #define CAN_ERR_BUSOFF       0x00000040U /* bus off */
 #define CAN_ERR_BUSERROR     0x00000080U /* bus error (may flood!) */
 #define CAN_ERR_RESTARTED    0x00000100U /* controller restarted */
+#define CAN_ERR_TRANSCEIVER  0x00000200U /* transceiver alert */
 
 /* arbitration lost in bit ... / data[0] */
 #define CAN_ERR_LOSTARB_UNSPEC   0x00 /* unspecified */
@@ -78,18 +79,20 @@
 #define CAN_ERR_PROT_LOC_INTERM  0x12 /* intermission */
 
 /* error status of CAN-transceiver / data[4] */
-/*                                             CANH CANL */
+/*                                             CANL CANH */
 #define CAN_ERR_TRX_UNSPEC             0x00 /* 0000 0000 */
 #define CAN_ERR_TRX_CANH_NO_WIRE       0x04 /* 0000 0100 */
 #define CAN_ERR_TRX_CANH_SHORT_TO_BAT  0x05 /* 0000 0101 */
 #define CAN_ERR_TRX_CANH_SHORT_TO_VCC  0x06 /* 0000 0110 */
 #define CAN_ERR_TRX_CANH_SHORT_TO_GND  0x07 /* 0000 0111 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_CANH 0x08 /* 0000 1000 */
 #define CAN_ERR_TRX_CANL_NO_WIRE       0x40 /* 0100 0000 */
 #define CAN_ERR_TRX_CANL_SHORT_TO_BAT  0x50 /* 0101 0000 */
 #define CAN_ERR_TRX_CANL_SHORT_TO_VCC  0x60 /* 0110 0000 */
 #define CAN_ERR_TRX_CANL_SHORT_TO_GND  0x70 /* 0111 0000 */
-#define CAN_ERR_TRX_CANL_SHORT_TO_CANH 0x80 /* 1000 0000 */
 
+#define CAN_ERR_TRX_UNKNOWN_ERROR      0xF0
+
 /* controller specific additional information / data[5..7] */
 
 #endif /* CAN_ERROR_H */
Index: include/socketcan/can/transceiver.h
===================================================================
--- include/socketcan/can/transceiver.h (revision 0)
+++ include/socketcan/can/transceiver.h (revision 0)
@@ -0,0 +1,99 @@
+/*
+ * socketcan/can/transceiver.h
+ *
+ * Definitions for the CAN transceiver class device interface
+ *
+ * Copyright (C) 2009 Kurt Van Dijck <[email protected]>
+ *
+ * Send feedback to <[email protected]>
+ */
+
+#include <linux/device.h>
+#include <linux/netdevice.h>
+
+#ifndef CAN_TRANSCEIVER_H
+#define CAN_TRANSCEIVER_H
+
+/*
+ * CAN transceiver modes
+ */
+#define CANTR_MODE_OFF 0
+#define CANTR_MODE_ON  1
+
+/*
+ * CAN transceiver class device
+ */
+struct can_transceiver {
+       int id;
+       struct device dev;
+       int (*get_max_bitrate)(struct can_transceiver *);
+       /* returns any CAN_ERR_TRX_ constants, or 0 when OK */
+       int (*get_state)(struct can_transceiver *);
+       /* mode is any CANTR_MODE_... */
+       int (*set_mode)(struct can_transceiver *, int mode);
+       /* match_name & netdev are protected by the global cantr_lock */
+       char *match_name;
+       struct net_device *netdev;
+       struct mutex lock; /* protects the next fields */
+       int mode;
+};
+#define to_can_transceiver(d) (container_of((d), struct can_transceiver, dev))
+
+static inline void cantr_set_drvdata(struct can_transceiver *cantr, void *p)
+{
+       dev_set_drvdata(&cantr->dev, p);
+}
+static inline void *cantr_get_drvdata(struct can_transceiver *cantr)
+{
+       return dev_get_drvdata(&cantr->dev);
+}
+
+/**
+ * can_transceiver_register - register w/ can_tranceiver class
+ * @cantr: the device to register
+ * @parent: the parent device
+ *
+ * Returns 0 on success
+ */
+extern int can_transceiver_register(struct can_transceiver *);
+
+/**
+ * can_transceiver_unregister - removes the can transceiver
+ *
+ * @cantr: the can transceiver class device to remove
+ */
+extern void can_transceiver_unregister(struct can_transceiver *);
+
+/**
+ * can_transceiver_set_match_name
+ * @cantr: the CAN transceiver to detach
+ * @name: the new name. may be 0.
+ *
+ * This function changes the name that the transceiver uses to match
+ * a net_device. Calling this function may change the associated
+ * net_device
+ */
+extern void can_transceiver_set_match_name(struct can_transceiver *,
+               const char *name);
+
+/*
+ * alert from can transceiver.
+ * the can transceiver core should do something with it
+ */
+extern void can_transceiver_alert(struct can_transceiver *cantr, int state);
+/*
+ * utility functions
+ */
+extern int can_transceiver_set_mode(struct can_transceiver *cantr, int mode);
+extern int can_transceiver_get_mode(struct can_transceiver *cantr);
+extern int can_transceiver_get_max_bitrate(struct can_transceiver *cantr);
+/**
+ * can_transceiver_get_state - get the current state of the transceiver
+ * @cantr: the can transceiver
+ *
+ * Returns: 0 for ready, -error for software failure, +num for hardware failure
+ */
+extern int can_transceiver_get_state(struct can_transceiver *cantr);
+
+#endif /* CAN_TRANSCEIVER_H */
+
Index: Makefile
===================================================================
--- Makefile    (revision 1069)
+++ Makefile    (working copy)
@@ -6,6 +6,9 @@
 
 export CONFIG_CAN_VCAN=m
 export CONFIG_CAN_SLCAN=m
+export CONFIG_CANTR_CORE=m
+export CONFIG_CANTR_PCA82C251=m
+export CONFIG_CANTR_TJA1041=m
 export CONFIG_CAN_DEV=m
 export CONFIG_CAN_CALC_BITTIMING=y
 #export CONFIG_CAN_DEV_SYSFS=y
Index: drivers/net/can/dev.c
===================================================================
--- drivers/net/can/dev.c       (revision 1069)
+++ drivers/net/can/dev.c       (working copy)
@@ -38,6 +38,7 @@
 #endif
 #include "sysfs.h"
 #endif
+#include <socketcan/can/transceiver.h>
 
 #define MOD_DESC "CAN device driver interface"
 
@@ -591,7 +592,12 @@
 #ifdef CONFIG_CAN_DEV_SYSFS
   int err;
 #endif
+#ifdef CONFIG_CANTR_CORE
+       int ret;
+       int max_bitrate;
+#endif
 
+
   if (!priv->bittiming.tq && !priv->bittiming.bitrate) {
      dev_err(ND2D(dev), "bit-timing not yet defined\n");
      return -EINVAL;
@@ -609,6 +615,20 @@
         return err;
   }
 #endif
+#ifdef CONFIG_CANTR_CORE
+       if (!priv->cantr)
+               goto no_transceiver;
+       max_bitrate = can_transceiver_get_max_bitrate(priv->cantr);
+       if (priv->bittiming.bitrate <= max_bitrate)
+               goto transceiver_bitrate_ok;
+       dev_err(ND2D(dev), "bitrate over transceiver's range\n");
+       return -EINVAL;
+transceiver_bitrate_ok:
+       ret = can_transceiver_set_mode(priv->cantr, CANTR_MODE_ON);
+       if (ret < 0)
+               return ret;
+no_transceiver:
+#endif
 
   /* Switch carrier on if device was stopped while in bus-off state */
   if (!netif_carrier_ok(dev))
@@ -633,6 +653,13 @@
   if (del_timer_sync(&priv->restart_timer))
      dev_put(dev);
   can_flush_echo_skb(dev);
+#ifdef CONFIG_CANTR_CORE
+       if (!priv->cantr)
+               goto no_transceiver;
+       can_transceiver_set_mode(priv->cantr, CANTR_MODE_OFF);
+no_transceiver:
+       ; /* remove warning 'label at end of ...' */
+#endif
 }
 EXPORT_SYMBOL_GPL(close_candev);
 
Index: drivers/net/can/Makefile
===================================================================
--- drivers/net/can/Makefile    (revision 1069)
+++ drivers/net/can/Makefile    (working copy)
@@ -34,6 +34,8 @@
 obj-$(CONFIG_CAN_VCAN)         += vcan.o
 obj-$(CONFIG_CAN_SLCAN)                += slcan.o
 
+obj-$(CONFIG_CANTR_CORE)       += transceiver/
+
 obj-$(CONFIG_CAN_DEV)          += can-dev.o
 can-dev-y                      := dev.o
 can-dev-$(CONFIG_CAN_DEV_SYSFS) += sysfs.o
@@ -60,5 +62,8 @@
 ifneq ($(CONFIG_CAN_CALC_BITTIMING),n)
   EXTRA_CFLAGS += -DCONFIG_CAN_CALC_BITTIMING
 endif
+ifneq ($(CONFIG_CANTR_CORE),n)
+       EXTRA_CFLAGS += -DCONFIG_CANTR_CORE
+endif
 
 endif
Index: drivers/net/can/Kconfig
===================================================================
--- drivers/net/can/Kconfig     (revision 1069)
+++ drivers/net/can/Kconfig     (working copy)
@@ -37,6 +37,29 @@
 source "drivers/net/can/old/Kconfig"
 endif
 
+config CANTR_CORE
+       tristate "CAN transceiver core"
+       depends on CAN
+       default Y
+       ---help---
+         Say y to have support for extended functions of various CAN 
transceivers.
+
+config CANTR_PCA82C251
+       tristate "NXP PCA82C251"
+       depends on CANTR_CORE
+       default Y
+       ---help---
+         this is the most used CAN transceiver. Drivers can use it directly or 
via
+         a platform device
+
+config CANTR_TJA1041
+       tristate "NXP TJA1041"
+       depends on CANTR_CORE
+       default Y
+       ---help---
+         This is an advanced CAN transceiver. It can detect short-circuits on 
the bus,
+         has error indications, ...
+
 config CAN_DEV
   tristate "Platform CAN drivers with Netlink support"
   depends on CAN
Index: drivers/net/can/transceiver/Makefile
===================================================================
--- drivers/net/can/transceiver/Makefile        (revision 0)
+++ drivers/net/can/transceiver/Makefile        (revision 0)
@@ -0,0 +1,22 @@
+ifeq ($(KERNELRELEASE),)
+
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD       := $(shell pwd)
+TOPDIR    := $(PWD)/../../../..
+
+modules modules_install clean:
+       $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+
+obj-$(CONFIG_CANTR_CORE)       += can-transceiver.o
+obj-$(CONFIG_CANTR_PCA82C251)  += pca82c251.o
+obj-$(CONFIG_CANTR_TJA1041)    += tja1041.o
+
+ifeq ($(CONFIG_CAN_DEBUG_DEVICES),y)
+       EXTRA_CFLAGS += -DDEBUG
+endif
+
+endif
Index: drivers/net/can/transceiver/can-transceiver.c
===================================================================
--- drivers/net/can/transceiver/can-transceiver.c       (revision 0)
+++ drivers/net/can/transceiver/can-transceiver.c       (revision 0)
@@ -0,0 +1,511 @@
+/*
+ * CAN transceiver subsystem, base class
+ *
+ * Copyright (C) 2009 EIA Electronics
+ * Author: Kurt Van Dijck <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/idr.h>
+#include <linux/sysfs.h>
+#include <linux/timer.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <socketcan/can.h>
+#include <socketcan/can/dev.h>
+#include <socketcan/can/error.h>
+#include <socketcan/can/transceiver.h>
+#include <net/rtnetlink.h>
+
+static DEFINE_IDR(cantr_idr);
+static DEFINE_MUTEX(cantr_lock);
+static struct class *cantr_class;
+
+int can_transceiver_get_state(struct can_transceiver *cantr)
+{
+       if (!cantr->get_state)
+               return 0;
+       return cantr->get_state(cantr);
+}
+EXPORT_SYMBOL_GPL(can_transceiver_get_state);
+
+int can_transceiver_get_mode(struct can_transceiver *cantr)
+{
+       int ret;
+
+       ret = mutex_lock_interruptible(&cantr->lock);
+       if (ret < 0)
+               return ret;
+       ret = cantr->mode;
+       mutex_unlock(&cantr->lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(can_transceiver_get_mode);
+
+int can_transceiver_set_mode(struct can_transceiver *cantr, int mode)
+{
+       int ret;
+
+       ret = mutex_lock_interruptible(&cantr->lock);
+       if (ret < 0)
+               return ret;
+
+       if (cantr->mode == mode)
+               goto done;
+
+       if (cantr->set_mode) {
+               ret = cantr->set_mode(cantr, mode);
+               if (ret < 0)
+                       goto failed;
+       }
+       if (mode && !cantr->mode)
+               get_device(&cantr->dev);
+       else if (!mode && cantr->mode)
+               put_device(&cantr->dev);
+
+       cantr->mode = mode;
+done:
+       mutex_unlock(&cantr->lock);
+       return mode;
+failed:
+       mutex_unlock(&cantr->lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(can_transceiver_set_mode);
+
+int can_transceiver_get_max_bitrate(struct can_transceiver *cantr)
+{
+       if (!cantr->get_max_bitrate)
+               /* must suppose it's ok, assume 1GBit as upper limit */
+               return 1000000000;
+       return cantr->get_max_bitrate(cantr);
+}
+EXPORT_SYMBOL_GPL(can_transceiver_get_max_bitrate);
+
+static void can_transceiver_notify(struct net_device *dev, int state)
+{
+       struct sk_buff *skb;
+       struct can_frame *cf;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+       struct net_device_stats *stats = can_get_stats(dev);
+#else
+       struct net_device_stats *stats = &dev->stats;
+#endif
+       /* send message upstream */
+       skb = alloc_can_err_skb(dev, &cf);
+       if (unlikely(!skb))
+               return;
+       cf->can_id |= CAN_ERR_TRANSCEIVER;
+       cf->data[4] = state;
+       netif_rx(skb);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
+       dev->last_rx = jiffies;
+#endif
+       stats->rx_packets++;
+       stats->rx_bytes += cf->can_dlc;
+}
+
+/*
+ * to be called from within can transceivers
+ * but not from interrupt context
+ * each transceiver should deal with that on his own way
+ * to avoid a lot of redundant work
+ */
+void can_transceiver_alert(struct can_transceiver *cantr, int state)
+{
+       if (!cantr->netdev)
+               return;
+       if (state < 0)
+               return;
+
+       can_transceiver_notify(cantr->netdev, state);
+
+       rtnl_lock();
+       dev_alert(&cantr->dev, "bring %s down\n", cantr->netdev->name);
+       dev_close(cantr->netdev);
+       rtnl_unlock();
+}
+EXPORT_SYMBOL_GPL(can_transceiver_alert);
+
+static int _can_transceiver_attach(struct can_transceiver *cantr,
+               struct net_device *netdev)
+{
+       struct can_priv *priv = netdev_priv(netdev);
+       int ret;
+       int initial_mode = 0;
+
+       if (netdev->type != ARPHRD_CAN)
+               return -EBADF;
+
+       if (!cantr) {
+               /* special case: cantr is supplied in netdev during create */
+               cantr = priv->cantr;
+               if (!cantr)
+                       return -EINVAL;
+               initial_mode = 1;
+       }
+       if (cantr->netdev) {
+               ret = -EBUSY;
+               goto failed;
+       }
+       if (!initial_mode && priv->cantr && (cantr != priv->cantr)) {
+               ret = -EBUSY;
+               goto failed;
+       }
+       cantr->netdev = netdev;
+       priv->cantr = cantr;
+       ret = sysfs_create_link(&cantr->dev.kobj, &netdev->dev.kobj,
+                       "netdev");
+       if (ret < 0)
+               goto failed;
+       ret = sysfs_create_link(&netdev->dev.kobj, &cantr->dev.kobj,
+                       "can-transceiver");
+       if (ret < 0)
+               goto failed;
+       dev_info(&cantr->dev, "attached to %s\n", netdev->name);
+
+       return 0;
+failed:
+       cantr->netdev = 0;
+       priv->cantr = 0;
+       sysfs_remove_link(&netdev->dev.kobj, "can-transceiver");
+       sysfs_remove_link(&cantr->dev.kobj, "netdev");
+       dev_err(&cantr->dev, "attach to %s failed\n", netdev->name);
+       return ret;
+}
+
+static void _can_transceiver_detach(struct can_transceiver *cantr)
+{
+       struct net_device *netdev;
+       struct can_priv *priv;
+
+       if (!cantr->netdev)
+               return;
+       netdev = cantr->netdev;
+       priv = netdev_priv(netdev);
+       priv->cantr = 0;
+       cantr->netdev = 0;
+       sysfs_remove_link(&netdev->dev.kobj, "can-transceiver");
+       sysfs_remove_link(&cantr->dev.kobj, "netdev");
+       dev_info(&cantr->dev, "detached from %s\n", netdev->name);
+}
+
+static int cantr_match_netdev(struct device *dev, void *p)
+{
+       struct can_transceiver *cantr = to_can_transceiver(dev);
+       struct net_device *netdev = p;
+       const char *name;
+
+       if (!cantr->match_name)
+               return 0;
+       if (!netdev->dev.parent)
+               return 0;
+       name = dev_name(netdev->dev.parent);
+       if (!name || !strlen(name))
+               return 0;
+       return !strcmp(name, cantr->match_name);
+}
+
+void can_transceiver_set_match_name(struct can_transceiver *cantr,
+               const char *name)
+{
+       struct net_device *netdev;
+       struct can_priv *priv;
+
+       if (name && !name[0])
+               name = 0;
+       mutex_lock(&cantr_lock);
+       /* remove current assignment (if there is one) */
+       netdev = cantr->netdev;
+       if (netdev) {
+               dev_hold(netdev);
+               _can_transceiver_detach(cantr);
+               dev_put(netdev);
+
+       }
+
+       kfree(cantr->match_name);
+       cantr->match_name = 0;
+       if (!name)
+               goto name_done;
+       cantr->match_name = kstrdup(name, GFP_KERNEL);
+
+       read_lock(&dev_base_lock);
+       for_each_netdev(&init_net, netdev) {
+               dev_hold(netdev);
+               if (!cantr_match_netdev(&cantr->dev, netdev))
+                       goto netdev_ignore;
+               if (netdev->type != ARPHRD_CAN)
+                       goto netdev_ignore;
+               priv = netdev_priv(netdev);
+               if (priv->cantr)
+                       goto netdev_ignore;
+               read_unlock(&dev_base_lock);
+               _can_transceiver_attach(cantr, netdev);
+               if (netif_running(netdev))
+                       can_transceiver_set_mode(cantr, CANTR_MODE_ON);
+               dev_put(netdev);
+               goto name_done;
+netdev_ignore:
+               dev_put(netdev);
+       }
+       read_unlock(&dev_base_lock);
+name_done:
+       mutex_unlock(&cantr_lock);
+       return;
+}
+EXPORT_SYMBOL_GPL(can_transceiver_set_match_name);
+
+/*
+ * notification callback
+ */
+static int can_transceiver_notifications(struct notifier_block *nb,
+               unsigned long msg, void *data)
+{
+       struct net_device *netdev = data;
+       struct can_priv *priv = netdev_priv(netdev);
+       struct device *dev;
+       struct can_transceiver *cantr;
+
+       if (netdev->type != ARPHRD_CAN)
+               return NOTIFY_DONE;
+
+       switch (msg) {
+       case NETDEV_REGISTER:
+               mutex_lock(&cantr_lock);
+               if (priv->cantr) {
+                       cantr = priv->cantr;
+                       if (!get_device(&cantr->dev))
+                               goto register_done;
+                       _can_transceiver_attach(0, netdev);
+                       goto attached;
+               }
+               dev = class_find_device(cantr_class, 0,
+                               netdev, cantr_match_netdev);
+               if (!dev)
+                       goto register_done;
+               cantr = to_can_transceiver(dev);
+               _can_transceiver_attach(cantr, netdev);
+attached:
+               /* class_find_device did get_device() */
+               put_device(&cantr->dev);
+register_done:
+               mutex_unlock(&cantr_lock);
+               break;
+
+       case NETDEV_UNREGISTER:
+               mutex_lock(&cantr_lock);
+               if (priv->cantr)
+                       _can_transceiver_detach(priv->cantr);
+               priv->cantr = 0;
+               mutex_unlock(&cantr_lock);
+               break;
+       }
+       return NOTIFY_DONE;
+}
+
+/* notifier block for netdevice event */
+static struct notifier_block can_transceiver_notifier __read_mostly = {
+       .notifier_call = can_transceiver_notifications,
+};
+
+/*
+ * sysfs
+ */
+static ssize_t show_max_bitrate(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       struct can_transceiver *cantr = to_can_transceiver(dev);
+
+       return sprintf(buf, "%i\n", can_transceiver_get_max_bitrate(cantr));
+}
+
+static ssize_t show_mode(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       struct can_transceiver *cantr = to_can_transceiver(dev);
+
+       return sprintf(buf, "%i\n", cantr->mode);
+}
+
+static ssize_t store_mode(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t len)
+{
+       struct can_transceiver *cantr = to_can_transceiver(dev);
+       unsigned long new;
+       int ret = -EINVAL;
+
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+
+       ret = strict_strtoul(buf, 0, &new);
+       if (ret < 0)
+               return ret;
+
+       ret = can_transceiver_set_mode(cantr, new);
+       if (ret < 0)
+               return ret;
+       return len;
+}
+
+static ssize_t show_cantr_state(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       struct can_transceiver *cantr = to_can_transceiver(dev);
+
+       return sprintf(buf, "%i\n", can_transceiver_get_state(cantr));
+}
+
+static ssize_t show_match(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       struct can_transceiver *cantr = to_can_transceiver(dev);
+
+       return sprintf(buf, "%s\n", cantr->match_name);
+}
+static ssize_t store_match(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t len)
+{
+       struct can_transceiver *cantr = to_can_transceiver(dev);
+       char *tmp, *p;
+
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+
+       tmp = kstrdup(buf, GFP_KERNEL);
+       for (p = &tmp[len-1]; (p >= tmp) && strchr(" \t\r\n\v\f", *p); --p)
+               *p = 0;
+       can_transceiver_set_match_name(cantr, tmp);
+       kfree(tmp);
+       return len;
+}
+
+static struct device_attribute class_dev_attrs[] = {
+       __ATTR(max_bitrate, S_IRUGO, show_max_bitrate, 0),
+       __ATTR(mode, S_IRUGO | S_IWUSR | S_IWGRP, show_mode, store_mode),
+       __ATTR(state, S_IRUGO, show_cantr_state, 0),
+       __ATTR(match, S_IRUGO | S_IWUSR, show_match, store_match),
+       { },
+};
+
+/*
+ * CLASS interface
+ */
+static void cantr_release(struct device *dev)
+{
+}
+
+int can_transceiver_register(struct can_transceiver *cantr)
+{
+       int id, ret;
+
+       if (idr_pre_get(&cantr_idr, GFP_KERNEL) == 0) {
+               ret = -ENOMEM;
+               goto fail_idr;
+       }
+       mutex_lock(&cantr_lock);
+       ret = idr_get_new(&cantr_idr, cantr, &id);
+       mutex_unlock(&cantr_lock);
+       if (ret < 0)
+               goto fail_idr;
+
+       id = id & MAX_ID_MASK;
+       cantr->id = id;
+       cantr->dev.class = cantr_class;
+       cantr->dev.release = cantr_release;
+       dev_set_name(&cantr->dev, "cantr%d", id);
+       mutex_init(&cantr->lock);
+       ret = device_register(&cantr->dev);
+       if (ret < 0)
+               goto fail_register;
+
+       dev_notice(&cantr->dev, "loaded [%s]\n",
+               cantr->dev.parent ? dev_name(cantr->dev.parent) : "<>");
+       return 0;
+
+fail_register:
+       dev_err(cantr->dev.parent,
+               "can transceiver core: unable to register, ret %i\n", ret);
+       mutex_lock(&cantr_lock);
+       idr_remove(&cantr_idr, id);
+       mutex_unlock(&cantr_lock);
+fail_idr:
+       return ret;
+}
+EXPORT_SYMBOL_GPL(can_transceiver_register);
+
+void can_transceiver_unregister(struct can_transceiver *cantr)
+{
+       struct net_device *netdev;
+       /* unregister device with lock, so no other CAN netdev can attach. */
+       mutex_lock(&cantr_lock);
+       netdev = cantr->netdev;
+       if (netdev) {
+               dev_hold(netdev);
+               _can_transceiver_detach(cantr);
+               dev_put(netdev);
+       }
+
+       device_unregister(&cantr->dev);
+       idr_remove(&cantr_idr, cantr->id);
+       mutex_unlock(&cantr_lock);
+
+       kfree(cantr->match_name);
+       cantr->match_name = 0;
+
+       dev_notice(&cantr->dev, "unloaded\n");
+}
+EXPORT_SYMBOL_GPL(can_transceiver_unregister);
+
+/*
+ * can_transceiver_start_mod - initialize can transceiver subsystem
+ */
+static int __init can_transceiver_start_mod(void)
+{
+       int ret;
+
+       BUG_ON(cantr_class);
+       cantr_class = class_create(THIS_MODULE, "can-transceiver");
+       if (IS_ERR(cantr_class)) {
+               printk(KERN_ERR "%s: couldn't create class\n", __FILE__);
+               ret = PTR_ERR(cantr_class);
+               goto fail_class;
+       }
+       cantr_class->dev_attrs = class_dev_attrs;
+       ret = register_netdevice_notifier(&can_transceiver_notifier);
+       if (ret < 0)
+               goto fail_notifier;
+       /*cantr_class->suspend = cantr_suspend;*/
+       /*cantr_class->resume = cantr_resume;*/
+       return 0;
+
+fail_notifier:
+       class_destroy(cantr_class);
+fail_class:
+       return ret;
+}
+
+/*
+ * can_transceiver_stop_mod - stop can transceiver subsystem
+ */
+static void __exit can_transceiver_stop_mod(void)
+{
+       unregister_netdevice_notifier(&can_transceiver_notifier);
+       class_destroy(cantr_class);
+}
+
+subsys_initcall(can_transceiver_start_mod);
+module_exit(can_transceiver_stop_mod);
+
+MODULE_AUTHOR("Kurt Van Dijck <[email protected]>");
+MODULE_DESCRIPTION("CAN transceiver class support");
+MODULE_LICENSE("GPL");
+
_______________________________________________
Socketcan-core mailing list
[email protected]
https://lists.berlios.de/mailman/listinfo/socketcan-core

Reply via email to