diff options
Diffstat (limited to 'drivers/s390/net/lcs.c')
| -rw-r--r-- | drivers/s390/net/lcs.c | 488 |
1 files changed, 320 insertions, 168 deletions
diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index 9bcfa04d863..0a7d87c372b 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -1,15 +1,12 @@ /* - * linux/drivers/s390/net/lcs.c - * * Linux for S/390 Lan Channel Station Network Driver * - * Copyright (C) 1999-2001 IBM Deutschland Entwicklung GmbH, - * IBM Corporation - * Author(s): Original Code written by - * DJ Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) - * Rewritten by - * Frank Pavlic (fpavlic@de.ibm.com) and - * Martin Schwidefsky <schwidefsky@de.ibm.com> + * Copyright IBM Corp. 1999, 2009 + * Author(s): Original Code written by + * DJ Barrow <djbarrow@de.ibm.com,barrow_dj@yahoo.com> + * Rewritten by + * Frank Pavlic <fpavlic@de.ibm.com> and + * Martin Schwidefsky <schwidefsky@de.ibm.com> * * 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 @@ -26,16 +23,20 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define KMSG_COMPONENT "lcs" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + #include <linux/module.h> #include <linux/if.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> -#include <linux/trdevice.h> #include <linux/fddidevice.h> #include <linux/inetdevice.h> #include <linux/in.h> #include <linux/igmp.h> #include <linux/delay.h> +#include <linux/kthread.h> +#include <linux/slab.h> #include <net/arp.h> #include <net/ip.h> @@ -46,22 +47,22 @@ #include <asm/ccwgroup.h> #include "lcs.h" -#include "cu3088.h" -#if !defined(CONFIG_NET_ETHERNET) && \ - !defined(CONFIG_TR) && !defined(CONFIG_FDDI) +#if !defined(CONFIG_ETHERNET) && !defined(CONFIG_FDDI) #error Cannot compile lcs.c without some net devices switched on. #endif -#define PRINTK_HEADER " lcs: " - /** * initialization string for output */ static char version[] __initdata = "LCS driver"; -static char debug_buffer[255]; + +/** + * the root device for lcs group devices + */ +static struct device *lcs_root_dev; /** * Some prototypes. @@ -69,12 +70,15 @@ static char debug_buffer[255]; static void lcs_tasklet(unsigned long); static void lcs_start_kernel_thread(struct work_struct *); static void lcs_get_frames_cb(struct lcs_channel *, struct lcs_buffer *); +#ifdef CONFIG_IP_MULTICAST static int lcs_send_delipm(struct lcs_card *, struct lcs_ipm_list *); +#endif /* CONFIG_IP_MULTICAST */ static int lcs_recovery(void *ptr); /** * Debug Facility Stuff */ +static char debug_buffer[255]; static debug_info_t *lcs_dbf_setup; static debug_info_t *lcs_dbf_trace; @@ -96,7 +100,7 @@ lcs_register_debug_facility(void) lcs_dbf_setup = debug_register("lcs_setup", 2, 1, 8); lcs_dbf_trace = debug_register("lcs_trace", 4, 1, 8); if (lcs_dbf_setup == NULL || lcs_dbf_trace == NULL) { - PRINT_ERR("Not enough memory for debug facility.\n"); + pr_err("Not enough memory for debug facility.\n"); lcs_unregister_debug_facility(); return -ENOMEM; } @@ -278,7 +282,7 @@ lcs_setup_write_ccws(struct lcs_card *card) LCS_DBF_TEXT(3, setup, "iwritccw"); /* Setup write ccws. */ - memset(card->write.ccws, 0, sizeof(struct ccw1) * LCS_NUM_BUFFS + 1); + memset(card->write.ccws, 0, sizeof(struct ccw1) * (LCS_NUM_BUFFS + 1)); for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) { card->write.ccws[cnt].cmd_code = LCS_CCW_WRITE; card->write.ccws[cnt].count = 0; @@ -492,7 +496,7 @@ lcs_start_channel(struct lcs_channel *channel) unsigned long flags; int rc; - LCS_DBF_TEXT_(4,trace,"ssch%s", channel->ccwdev->dev.bus_id); + LCS_DBF_TEXT_(4, trace,"ssch%s", dev_name(&channel->ccwdev->dev)); spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); rc = ccw_device_start(channel->ccwdev, channel->ccws + channel->io_idx, 0, 0, @@ -501,8 +505,11 @@ lcs_start_channel(struct lcs_channel *channel) channel->state = LCS_CH_STATE_RUNNING; spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); if (rc) { - LCS_DBF_TEXT_(4,trace,"essh%s", channel->ccwdev->dev.bus_id); - PRINT_ERR("Error in starting channel, rc=%d!\n", rc); + LCS_DBF_TEXT_(4,trace,"essh%s", + dev_name(&channel->ccwdev->dev)); + dev_err(&channel->ccwdev->dev, + "Starting an LCS device resulted in an error," + " rc=%d!\n", rc); } return rc; } @@ -514,12 +521,13 @@ lcs_clear_channel(struct lcs_channel *channel) int rc; LCS_DBF_TEXT(4,trace,"clearch"); - LCS_DBF_TEXT_(4,trace,"%s", channel->ccwdev->dev.bus_id); + LCS_DBF_TEXT_(4, trace, "%s", dev_name(&channel->ccwdev->dev)); spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); rc = ccw_device_clear(channel->ccwdev, (addr_t) channel); spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); if (rc) { - LCS_DBF_TEXT_(4,trace,"ecsc%s", channel->ccwdev->dev.bus_id); + LCS_DBF_TEXT_(4, trace, "ecsc%s", + dev_name(&channel->ccwdev->dev)); return rc; } wait_event(channel->wait_q, (channel->state == LCS_CH_STATE_CLEARED)); @@ -540,13 +548,14 @@ lcs_stop_channel(struct lcs_channel *channel) if (channel->state == LCS_CH_STATE_STOPPED) return 0; LCS_DBF_TEXT(4,trace,"haltsch"); - LCS_DBF_TEXT_(4,trace,"%s", channel->ccwdev->dev.bus_id); + LCS_DBF_TEXT_(4, trace, "%s", dev_name(&channel->ccwdev->dev)); channel->state = LCS_CH_STATE_INIT; spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); rc = ccw_device_halt(channel->ccwdev, (addr_t) channel); spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); if (rc) { - LCS_DBF_TEXT_(4,trace,"ehsc%s", channel->ccwdev->dev.bus_id); + LCS_DBF_TEXT_(4, trace, "ehsc%s", + dev_name(&channel->ccwdev->dev)); return rc; } /* Asynchronous halt initialted. Wait for its completion. */ @@ -632,11 +641,14 @@ __lcs_resume_channel(struct lcs_channel *channel) return 0; if (channel->ccws[channel->io_idx].flags & CCW_FLAG_SUSPEND) return 0; - LCS_DBF_TEXT_(5, trace, "rsch%s", channel->ccwdev->dev.bus_id); + LCS_DBF_TEXT_(5, trace, "rsch%s", dev_name(&channel->ccwdev->dev)); rc = ccw_device_resume(channel->ccwdev); if (rc) { - LCS_DBF_TEXT_(4, trace, "ersc%s", channel->ccwdev->dev.bus_id); - PRINT_ERR("Error in lcs_resume_channel: rc=%d\n",rc); + LCS_DBF_TEXT_(4, trace, "ersc%s", + dev_name(&channel->ccwdev->dev)); + dev_err(&channel->ccwdev->dev, + "Sending data from the LCS device to the LAN failed" + " with rc=%d\n",rc); } else channel->state = LCS_CH_STATE_RUNNING; return rc; @@ -826,7 +838,7 @@ lcs_notify_lancmd_waiters(struct lcs_card *card, struct lcs_cmd *cmd) } /** - * Emit buffer of a lan comand. + * Emit buffer of a lan command. */ static void lcs_lancmd_timeout(unsigned long data) @@ -880,13 +892,14 @@ lcs_send_lancmd(struct lcs_card *card, struct lcs_buffer *buffer, rc = lcs_ready_buffer(&card->write, buffer); if (rc) return rc; - init_timer(&timer); + init_timer_on_stack(&timer); timer.function = lcs_lancmd_timeout; timer.data = (unsigned long) reply; timer.expires = jiffies + HZ*card->lancmd_timeout; add_timer(&timer); wait_event(reply->wait_q, reply->received); del_timer_sync(&timer); + destroy_timer_on_stack(&timer); LCS_DBF_TEXT_(4, trace, "rc:%d",reply->rc); rc = reply->rc; lcs_put_reply(reply); @@ -1082,7 +1095,7 @@ lcs_check_multicast_support(struct lcs_card *card) cmd->cmd.lcs_qipassist.num_ip_pairs = 1; rc = lcs_send_lancmd(card, buffer, __lcs_check_multicast_cb); if (rc != 0) { - PRINT_ERR("Query IPAssist failed. Assuming unsupported!\n"); + pr_err("Query IPAssist failed. Assuming unsupported!\n"); return -EOPNOTSUPP; } if (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) @@ -1108,15 +1121,15 @@ list_modified: list_for_each_entry_safe(ipm, tmp, &card->ipm_list, list){ switch (ipm->ipm_state) { case LCS_IPM_STATE_SET_REQUIRED: - /* del from ipm_list so noone else can tamper with + /* del from ipm_list so no one else can tamper with * this entry */ list_del_init(&ipm->list); spin_unlock_irqrestore(&card->ipm_lock, flags); rc = lcs_send_setipm(card, ipm); spin_lock_irqsave(&card->ipm_lock, flags); if (rc) { - PRINT_INFO("Adding multicast address failed. " - "Table possibly full!\n"); + pr_info("Adding multicast address failed." + " Table possibly full!\n"); /* store ipm in failed list -> will be added * to ipm_list again, so a retry will be done * during the next call of this function */ @@ -1152,10 +1165,7 @@ static void lcs_get_mac_for_ipm(__be32 ipm, char *mac, struct net_device *dev) { LCS_DBF_TEXT(4,trace, "getmac"); - if (dev->type == ARPHRD_IEEE802_TR) - ip_tr_mc_map(ipm, mac); - else - ip_eth_mc_map(ipm, mac); + ip_eth_mc_map(ipm, mac); } /** @@ -1174,7 +1184,8 @@ lcs_remove_mc_addresses(struct lcs_card *card, struct in_device *in4_dev) spin_lock_irqsave(&card->ipm_lock, flags); list_for_each(l, &card->ipm_list) { ipm = list_entry(l, struct lcs_ipm_list, list); - for (im4 = in4_dev->mc_list; im4 != NULL; im4 = im4->next) { + for (im4 = rcu_dereference(in4_dev->mc_list); + im4 != NULL; im4 = rcu_dereference(im4->next_rcu)) { lcs_get_mac_for_ipm(im4->multiaddr, buf, card->dev); if ( (ipm->ipm.ip_addr == im4->multiaddr) && (memcmp(buf, &ipm->ipm.mac_addr, @@ -1219,16 +1230,16 @@ lcs_set_mc_addresses(struct lcs_card *card, struct in_device *in4_dev) unsigned long flags; LCS_DBF_TEXT(4, trace, "setmclst"); - for (im4 = in4_dev->mc_list; im4; im4 = im4->next) { + for (im4 = rcu_dereference(in4_dev->mc_list); im4 != NULL; + im4 = rcu_dereference(im4->next_rcu)) { lcs_get_mac_for_ipm(im4->multiaddr, buf, card->dev); ipm = lcs_check_addr_entry(card, im4, buf); if (ipm != NULL) continue; /* Address already in list. */ - ipm = (struct lcs_ipm_list *) - kzalloc(sizeof(struct lcs_ipm_list), GFP_ATOMIC); + ipm = kzalloc(sizeof(struct lcs_ipm_list), GFP_ATOMIC); if (ipm == NULL) { - PRINT_INFO("Not enough memory to add " - "new multicast entry!\n"); + pr_info("Not enough memory to add" + " new multicast entry!\n"); break; } memcpy(&ipm->ipm.mac_addr, buf, LCS_MAC_LENGTH); @@ -1248,7 +1259,6 @@ lcs_register_mc_addresses(void *data) struct in_device *in4_dev; card = (struct lcs_card *) data; - daemonize("regipm"); if (!lcs_do_run_thread(card, LCS_SET_MC_THREAD)) return 0; @@ -1257,10 +1267,10 @@ lcs_register_mc_addresses(void *data) in4_dev = in_dev_get(card->dev); if (in4_dev == NULL) goto out; - read_lock(&in4_dev->mc_list_lock); + rcu_read_lock(); lcs_remove_mc_addresses(card,in4_dev); lcs_set_mc_addresses(card, in4_dev); - read_unlock(&in4_dev->mc_list_lock); + rcu_read_unlock(); in_dev_put(in4_dev); netif_carrier_off(card->dev); @@ -1276,6 +1286,8 @@ out: lcs_clear_thread_running_bit(card, LCS_SET_MC_THREAD); return 0; } +#endif /* CONFIG_IP_MULTICAST */ + /** * function called by net device to * handle multicast address relevant things @@ -1283,16 +1295,16 @@ out: static void lcs_set_multicast_list(struct net_device *dev) { +#ifdef CONFIG_IP_MULTICAST struct lcs_card *card; LCS_DBF_TEXT(4, trace, "setmulti"); - card = (struct lcs_card *) dev->priv; + card = (struct lcs_card *) dev->ml_priv; if (!lcs_set_thread_start_bit(card, LCS_SET_MC_THREAD)) schedule_work(&card->kernel_thread_starter); -} - #endif /* CONFIG_IP_MULTICAST */ +} static long lcs_check_irb_error(struct ccw_device *cdev, struct irb *irb) @@ -1302,18 +1314,21 @@ lcs_check_irb_error(struct ccw_device *cdev, struct irb *irb) switch (PTR_ERR(irb)) { case -EIO: - PRINT_WARN("i/o-error on device %s\n", cdev->dev.bus_id); + dev_warn(&cdev->dev, + "An I/O-error occurred on the LCS device\n"); LCS_DBF_TEXT(2, trace, "ckirberr"); LCS_DBF_TEXT_(2, trace, " rc%d", -EIO); break; case -ETIMEDOUT: - PRINT_WARN("timeout on device %s\n", cdev->dev.bus_id); + dev_warn(&cdev->dev, + "A command timed out on the LCS device\n"); LCS_DBF_TEXT(2, trace, "ckirberr"); LCS_DBF_TEXT_(2, trace, " rc%d", -ETIMEDOUT); break; default: - PRINT_WARN("unknown error %ld on device %s\n", PTR_ERR(irb), - cdev->dev.bus_id); + dev_warn(&cdev->dev, + "An error occurred on the LCS device, rc=%ld\n", + PTR_ERR(irb)); LCS_DBF_TEXT(2, trace, "ckirberr"); LCS_DBF_TEXT(2, trace, " rc???"); } @@ -1390,7 +1405,7 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) cstat = irb->scsw.cmd.cstat; dstat = irb->scsw.cmd.dstat; - LCS_DBF_TEXT_(5, trace, "Rint%s",cdev->dev.bus_id); + LCS_DBF_TEXT_(5, trace, "Rint%s", dev_name(&cdev->dev)); LCS_DBF_TEXT_(5, trace, "%4x%4x", irb->scsw.cmd.cstat, irb->scsw.cmd.dstat); LCS_DBF_TEXT_(5, trace, "%4x%4x", irb->scsw.cmd.fctl, @@ -1399,8 +1414,10 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) /* Check for channel and device errors presented */ rc = lcs_get_problem(cdev, irb); if (rc || (dstat & DEV_STAT_UNIT_EXCEP)) { - PRINT_WARN("check on device %s, dstat=0x%X, cstat=0x%X \n", - cdev->dev.bus_id, dstat, cstat); + dev_warn(&cdev->dev, + "The LCS device stopped because of an error," + " dstat=0x%X, cstat=0x%X \n", + dstat, cstat); if (rc) { channel->state = LCS_CH_STATE_ERROR; } @@ -1460,10 +1477,9 @@ lcs_tasklet(unsigned long data) struct lcs_channel *channel; struct lcs_buffer *iob; int buf_idx; - int rc; channel = (struct lcs_channel *) data; - LCS_DBF_TEXT_(5, trace, "tlet%s",channel->ccwdev->dev.bus_id); + LCS_DBF_TEXT_(5, trace, "tlet%s", dev_name(&channel->ccwdev->dev)); /* Check for processed buffers. */ iob = channel->iob; @@ -1477,14 +1493,11 @@ lcs_tasklet(unsigned long data) channel->buf_idx = buf_idx; if (channel->state == LCS_CH_STATE_STOPPED) - // FIXME: what if rc != 0 ?? - rc = lcs_start_channel(channel); + lcs_start_channel(channel); spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); if (channel->state == LCS_CH_STATE_SUSPENDED && - channel->iob[channel->io_idx].state == LCS_BUF_STATE_READY) { - // FIXME: what if rc != 0 ?? - rc = __lcs_resume_channel(channel); - } + channel->iob[channel->io_idx].state == LCS_BUF_STATE_READY) + __lcs_resume_channel(channel); spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); /* Something happened on the channel. Wake up waiters. */ @@ -1538,24 +1551,24 @@ __lcs_start_xmit(struct lcs_card *card, struct sk_buff *skb, struct net_device *dev) { struct lcs_header *header; - int rc = 0; + int rc = NETDEV_TX_OK; LCS_DBF_TEXT(5, trace, "hardxmit"); if (skb == NULL) { card->stats.tx_dropped++; card->stats.tx_errors++; - return -EIO; + return NETDEV_TX_OK; } if (card->state != DEV_STATE_UP) { dev_kfree_skb(skb); card->stats.tx_dropped++; card->stats.tx_errors++; card->stats.tx_carrier_errors++; - return 0; + return NETDEV_TX_OK; } if (skb->protocol == htons(ETH_P_IPV6)) { dev_kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } netif_stop_queue(card->dev); spin_lock(&card->lock); @@ -1569,7 +1582,7 @@ __lcs_start_xmit(struct lcs_card *card, struct sk_buff *skb, card->tx_buffer = lcs_get_buffer(&card->write); if (card->tx_buffer == NULL) { card->stats.tx_dropped++; - rc = -EBUSY; + rc = NETDEV_TX_BUSY; goto out; } card->tx_buffer->callback = lcs_txbuffer_cb; @@ -1603,7 +1616,7 @@ lcs_start_xmit(struct sk_buff *skb, struct net_device *dev) int rc; LCS_DBF_TEXT(5, trace, "pktxmit"); - card = (struct lcs_card *) dev->priv; + card = (struct lcs_card *) dev->ml_priv; rc = __lcs_start_xmit(card, skb, dev); return rc; } @@ -1617,19 +1630,13 @@ lcs_startlan_auto(struct lcs_card *card) int rc; LCS_DBF_TEXT(2, trace, "strtauto"); -#ifdef CONFIG_NET_ETHERNET +#ifdef CONFIG_ETHERNET card->lan_type = LCS_FRAME_TYPE_ENET; rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP); if (rc == 0) return 0; #endif -#ifdef CONFIG_TR - card->lan_type = LCS_FRAME_TYPE_TR; - rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP); - if (rc == 0) - return 0; -#endif #ifdef CONFIG_FDDI card->lan_type = LCS_FRAME_TYPE_FDDI; rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP); @@ -1735,11 +1742,10 @@ lcs_start_kernel_thread(struct work_struct *work) struct lcs_card *card = container_of(work, struct lcs_card, kernel_thread_starter); LCS_DBF_TEXT(5, trace, "krnthrd"); if (lcs_do_start_thread(card, LCS_RECOVERY_THREAD)) - kernel_thread(lcs_recovery, (void *) card, SIGCHLD); + kthread_run(lcs_recovery, card, "lcs_recover"); #ifdef CONFIG_IP_MULTICAST if (lcs_do_start_thread(card, LCS_SET_MC_THREAD)) - kernel_thread(lcs_register_mc_addresses, - (void *) card, SIGCHLD); + kthread_run(lcs_register_mc_addresses, card, "regipm"); #endif } @@ -1757,8 +1763,8 @@ lcs_get_control(struct lcs_card *card, struct lcs_cmd *cmd) lcs_schedule_recovery(card); break; case LCS_CMD_STOPLAN: - PRINT_WARN("Stoplan for %s initiated by LGW.\n", - card->dev->name); + pr_warning("Stoplan for %s initiated by LGW.\n", + card->dev->name); if (card->dev) netif_carrier_off(card->dev); break; @@ -1786,7 +1792,8 @@ lcs_get_skb(struct lcs_card *card, char *skb_data, unsigned int skb_len) skb = dev_alloc_skb(skb_len); if (skb == NULL) { - PRINT_ERR("LCS: alloc_skb failed for device=%s\n", + dev_err(&card->dev->dev, + " Allocating a socket buffer to interface %s failed\n", card->dev->name); card->stats.rx_dropped++; return; @@ -1859,7 +1866,7 @@ lcs_getstats(struct net_device *dev) struct lcs_card *card; LCS_DBF_TEXT(4, trace, "netstats"); - card = (struct lcs_card *) dev->priv; + card = (struct lcs_card *) dev->ml_priv; return &card->stats; } @@ -1874,7 +1881,7 @@ lcs_stop_device(struct net_device *dev) int rc; LCS_DBF_TEXT(2, trace, "stopdev"); - card = (struct lcs_card *) dev->priv; + card = (struct lcs_card *) dev->ml_priv; netif_carrier_off(dev); netif_tx_disable(dev); dev->flags &= ~IFF_UP; @@ -1882,7 +1889,8 @@ lcs_stop_device(struct net_device *dev) (card->write.state != LCS_CH_STATE_RUNNING)); rc = lcs_stopcard(card); if (rc) - PRINT_ERR("Try it again!\n "); + dev_err(&card->dev->dev, + " Shutting down the LCS device failed\n "); return rc; } @@ -1897,11 +1905,11 @@ lcs_open_device(struct net_device *dev) int rc; LCS_DBF_TEXT(2, trace, "opendev"); - card = (struct lcs_card *) dev->priv; + card = (struct lcs_card *) dev->ml_priv; /* initialize statistics */ rc = lcs_detect(card); if (rc) { - PRINT_ERR("LCS:Error in opening device!\n"); + pr_err("Error in opening device!\n"); } else { dev->flags |= IFF_UP; @@ -1920,7 +1928,7 @@ lcs_portno_show (struct device *dev, struct device_attribute *attr, char *buf) { struct lcs_card *card; - card = (struct lcs_card *)dev->driver_data; + card = dev_get_drvdata(dev); if (!card) return 0; @@ -1935,14 +1943,16 @@ static ssize_t lcs_portno_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct lcs_card *card; - int value; + int value, rc; - card = (struct lcs_card *)dev->driver_data; + card = dev_get_drvdata(dev); if (!card) return 0; - sscanf(buf, "%u", &value); + rc = sscanf(buf, "%d", &value); + if (rc != 1) + return -EINVAL; /* TODO: sanity checks */ card->portno = value; @@ -1952,6 +1962,15 @@ lcs_portno_store (struct device *dev, struct device_attribute *attr, const char static DEVICE_ATTR(portno, 0644, lcs_portno_show, lcs_portno_store); +static const char *lcs_type[] = { + "not a channel", + "2216 parallel", + "2216 channel", + "OSA LCS card", + "unknown channel type", + "unsupported channel type", +}; + static ssize_t lcs_type_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1961,7 +1980,7 @@ lcs_type_show(struct device *dev, struct device_attribute *attr, char *buf) if (!cgdev) return -ENODEV; - return sprintf(buf, "%s\n", cu3088_type[cgdev->cdev[0]->id.driver_info]); + return sprintf(buf, "%s\n", lcs_type[cgdev->cdev[0]->id.driver_info]); } static DEVICE_ATTR(type, 0444, lcs_type_show, NULL); @@ -1971,7 +1990,7 @@ lcs_timeout_show(struct device *dev, struct device_attribute *attr, char *buf) { struct lcs_card *card; - card = (struct lcs_card *)dev->driver_data; + card = dev_get_drvdata(dev); return card ? sprintf(buf, "%u\n", card->lancmd_timeout) : 0; } @@ -1980,14 +1999,17 @@ static ssize_t lcs_timeout_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct lcs_card *card; - int value; + unsigned int value; + int rc; - card = (struct lcs_card *)dev->driver_data; + card = dev_get_drvdata(dev); if (!card) return 0; - sscanf(buf, "%u", &value); + rc = sscanf(buf, "%u", &value); + if (rc != 1) + return -EINVAL; /* TODO: sanity checks */ card->lancmd_timeout = value; @@ -2001,7 +2023,7 @@ static ssize_t lcs_dev_recover_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct lcs_card *card = dev->driver_data; + struct lcs_card *card = dev_get_drvdata(dev); char *tmp; int i; @@ -2024,10 +2046,17 @@ static struct attribute * lcs_attrs[] = { &dev_attr_recover.attr, NULL, }; - static struct attribute_group lcs_attr_group = { .attrs = lcs_attrs, }; +static const struct attribute_group *lcs_attr_groups[] = { + &lcs_attr_group, + NULL, +}; +static const struct device_type lcs_devtype = { + .name = "lcs", + .groups = lcs_attr_groups, +}; /** * lcs_probe_device is called on establishing a new ccwgroup_device. @@ -2036,7 +2065,6 @@ static int lcs_probe_device(struct ccwgroup_device *ccwgdev) { struct lcs_card *card; - int ret; if (!get_device(&ccwgdev->dev)) return -ENODEV; @@ -2048,13 +2076,7 @@ lcs_probe_device(struct ccwgroup_device *ccwgdev) put_device(&ccwgdev->dev); return -ENOMEM; } - ret = sysfs_create_group(&ccwgdev->dev.kobj, &lcs_attr_group); - if (ret) { - lcs_free_card(card); - put_device(&ccwgdev->dev); - return ret; - } - ccwgdev->dev.driver_data = card; + dev_set_drvdata(&ccwgdev->dev, card); ccwgdev->cdev[0]->handler = lcs_irq; ccwgdev->cdev[1]->handler = lcs_irq; card->gdev = ccwgdev; @@ -2062,7 +2084,9 @@ lcs_probe_device(struct ccwgroup_device *ccwgdev) card->thread_start_mask = 0; card->thread_allowed_mask = 0; card->thread_running_mask = 0; - return 0; + ccwgdev->dev.type = &lcs_devtype; + + return 0; } static int @@ -2071,7 +2095,7 @@ lcs_register_netdev(struct ccwgroup_device *ccwgdev) struct lcs_card *card; LCS_DBF_TEXT(2, setup, "regnetdv"); - card = (struct lcs_card *)ccwgdev->dev.driver_data; + card = dev_get_drvdata(&ccwgdev->dev); if (card->dev->reg_state != NETREG_UNINITIALIZED) return 0; SET_NETDEV_DEV(card->dev, &ccwgdev->dev); @@ -2081,6 +2105,20 @@ lcs_register_netdev(struct ccwgroup_device *ccwgdev) /** * lcs_new_device will be called by setting the group device online. */ +static const struct net_device_ops lcs_netdev_ops = { + .ndo_open = lcs_open_device, + .ndo_stop = lcs_stop_device, + .ndo_get_stats = lcs_getstats, + .ndo_start_xmit = lcs_start_xmit, +}; + +static const struct net_device_ops lcs_mc_netdev_ops = { + .ndo_open = lcs_open_device, + .ndo_stop = lcs_stop_device, + .ndo_get_stats = lcs_getstats, + .ndo_start_xmit = lcs_start_xmit, + .ndo_set_rx_mode = lcs_set_multicast_list, +}; static int lcs_new_device(struct ccwgroup_device *ccwgdev) @@ -2090,7 +2128,7 @@ lcs_new_device(struct ccwgroup_device *ccwgdev) enum lcs_dev_states recover_state; int rc; - card = (struct lcs_card *)ccwgdev->dev.driver_data; + card = dev_get_drvdata(&ccwgdev->dev); if (!card) return -ENODEV; @@ -2100,8 +2138,12 @@ lcs_new_device(struct ccwgroup_device *ccwgdev) card->write.ccwdev = ccwgdev->cdev[1]; recover_state = card->state; - ccw_device_set_online(card->read.ccwdev); - ccw_device_set_online(card->write.ccwdev); + rc = ccw_device_set_online(card->read.ccwdev); + if (rc) + goto out_err; + rc = ccw_device_set_online(card->write.ccwdev); + if (rc) + goto out_werr; LCS_DBF_TEXT(3, setup, "lcsnewdv"); @@ -2109,8 +2151,9 @@ lcs_new_device(struct ccwgroup_device *ccwgdev) rc = lcs_detect(card); if (rc) { LCS_DBF_TEXT(2, setup, "dtctfail"); - PRINT_WARN("Detection of LCS card failed with return code " - "%d (0x%x)\n", rc, rc); + dev_err(&card->dev->dev, + "Detecting a network adapter for LCS devices" + " failed with rc=%d (0x%x)\n", rc, rc); lcs_stopcard(card); goto out; } @@ -2120,18 +2163,12 @@ lcs_new_device(struct ccwgroup_device *ccwgdev) goto netdev_out; } switch (card->lan_type) { -#ifdef CONFIG_NET_ETHERNET +#ifdef CONFIG_ETHERNET case LCS_FRAME_TYPE_ENET: card->lan_type_trans = eth_type_trans; dev = alloc_etherdev(0); break; #endif -#ifdef CONFIG_TR - case LCS_FRAME_TYPE_TR: - card->lan_type_trans = tr_type_trans; - dev = alloc_trdev(0); - break; -#endif #ifdef CONFIG_FDDI case LCS_FRAME_TYPE_FDDI: card->lan_type_trans = fddi_type_trans; @@ -2140,21 +2177,18 @@ lcs_new_device(struct ccwgroup_device *ccwgdev) #endif default: LCS_DBF_TEXT(3, setup, "errinit"); - PRINT_ERR("LCS: Initialization failed\n"); + pr_err(" Initialization failed\n"); goto out; } if (!dev) goto out; card->dev = dev; - card->dev->priv = card; - card->dev->open = lcs_open_device; - card->dev->stop = lcs_stop_device; - card->dev->hard_start_xmit = lcs_start_xmit; - card->dev->get_stats = lcs_getstats; + card->dev->ml_priv = card; + card->dev->netdev_ops = &lcs_netdev_ops; memcpy(card->dev->dev_addr, card->mac, LCS_MAC_LENGTH); #ifdef CONFIG_IP_MULTICAST if (!lcs_check_multicast_support(card)) - card->dev->set_multicast_list = lcs_set_multicast_list; + card->dev->netdev_ops = &lcs_mc_netdev_ops; #endif netdev_out: lcs_set_allowed_threads(card,0xffffffff); @@ -2172,18 +2206,20 @@ netdev_out: goto out; /* Print out supported assists: IPv6 */ - PRINT_INFO("LCS device %s %s IPv6 support\n", card->dev->name, - (card->ip_assists_supported & LCS_IPASS_IPV6_SUPPORT) ? - "with" : "without"); + pr_info("LCS device %s %s IPv6 support\n", card->dev->name, + (card->ip_assists_supported & LCS_IPASS_IPV6_SUPPORT) ? + "with" : "without"); /* Print out supported assist: Multicast */ - PRINT_INFO("LCS device %s %s Multicast support\n", card->dev->name, - (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) ? - "with" : "without"); + pr_info("LCS device %s %s Multicast support\n", card->dev->name, + (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) ? + "with" : "without"); return 0; out: - ccw_device_set_offline(card->read.ccwdev); ccw_device_set_offline(card->write.ccwdev); +out_werr: + ccw_device_set_offline(card->read.ccwdev); +out_err: return -ENODEV; } @@ -2195,10 +2231,10 @@ __lcs_shutdown_device(struct ccwgroup_device *ccwgdev, int recovery_mode) { struct lcs_card *card; enum lcs_dev_states recover_state; - int ret; + int ret = 0, ret2 = 0, ret3 = 0; LCS_DBF_TEXT(3, setup, "shtdndev"); - card = (struct lcs_card *)ccwgdev->dev.driver_data; + card = dev_get_drvdata(&ccwgdev->dev); if (!card) return -ENODEV; if (recovery_mode == 0) { @@ -2210,13 +2246,15 @@ __lcs_shutdown_device(struct ccwgroup_device *ccwgdev, int recovery_mode) recover_state = card->state; ret = lcs_stop_device(card->dev); - ret = ccw_device_set_offline(card->read.ccwdev); - ret = ccw_device_set_offline(card->write.ccwdev); + ret2 = ccw_device_set_offline(card->read.ccwdev); + ret3 = ccw_device_set_offline(card->write.ccwdev); + if (!ret) + ret = (ret2) ? ret2 : ret3; + if (ret) + LCS_DBF_TEXT_(3, setup, "1err:%d", ret); if (recover_state == DEV_STATE_UP) { card->state = DEV_STATE_RECOVER; } - if (ret) - return ret; return 0; } @@ -2237,22 +2275,22 @@ lcs_recovery(void *ptr) int rc; card = (struct lcs_card *) ptr; - daemonize("lcs_recover"); LCS_DBF_TEXT(4, trace, "recover1"); if (!lcs_do_run_thread(card, LCS_RECOVERY_THREAD)) return 0; LCS_DBF_TEXT(4, trace, "recover2"); gdev = card->gdev; - PRINT_WARN("Recovery of device %s started...\n", gdev->dev.bus_id); + dev_warn(&gdev->dev, + "A recovery process has been started for the LCS device\n"); rc = __lcs_shutdown_device(gdev, 1); rc = lcs_new_device(gdev); if (!rc) - PRINT_INFO("Device %s successfully recovered!\n", - card->dev->name); + pr_info("Device %s successfully recovered!\n", + card->dev->name); else - PRINT_INFO("Device %s could not be recovered!\n", - card->dev->name); + pr_info("Device %s could not be recovered!\n", + card->dev->name); lcs_clear_thread_running_bit(card, LCS_RECOVERY_THREAD); return 0; } @@ -2265,7 +2303,7 @@ lcs_remove_device(struct ccwgroup_device *ccwgdev) { struct lcs_card *card; - card = (struct lcs_card *)ccwgdev->dev.driver_data; + card = dev_get_drvdata(&ccwgdev->dev); if (!card) return; @@ -2276,24 +2314,123 @@ lcs_remove_device(struct ccwgroup_device *ccwgdev) } if (card->dev) unregister_netdev(card->dev); - sysfs_remove_group(&ccwgdev->dev.kobj, &lcs_attr_group); lcs_cleanup_card(card); lcs_free_card(card); + dev_set_drvdata(&ccwgdev->dev, NULL); put_device(&ccwgdev->dev); } +static int lcs_pm_suspend(struct lcs_card *card) +{ + if (card->dev) + netif_device_detach(card->dev); + lcs_set_allowed_threads(card, 0); + lcs_wait_for_threads(card, 0xffffffff); + if (card->state != DEV_STATE_DOWN) + __lcs_shutdown_device(card->gdev, 1); + return 0; +} + +static int lcs_pm_resume(struct lcs_card *card) +{ + int rc = 0; + + if (card->state == DEV_STATE_RECOVER) + rc = lcs_new_device(card->gdev); + if (card->dev) + netif_device_attach(card->dev); + if (rc) { + dev_warn(&card->gdev->dev, "The lcs device driver " + "failed to recover the device\n"); + } + return rc; +} + +static int lcs_prepare(struct ccwgroup_device *gdev) +{ + return 0; +} + +static void lcs_complete(struct ccwgroup_device *gdev) +{ + return; +} + +static int lcs_freeze(struct ccwgroup_device *gdev) +{ + struct lcs_card *card = dev_get_drvdata(&gdev->dev); + return lcs_pm_suspend(card); +} + +static int lcs_thaw(struct ccwgroup_device *gdev) +{ + struct lcs_card *card = dev_get_drvdata(&gdev->dev); + return lcs_pm_resume(card); +} + +static int lcs_restore(struct ccwgroup_device *gdev) +{ + struct lcs_card *card = dev_get_drvdata(&gdev->dev); + return lcs_pm_resume(card); +} + +static struct ccw_device_id lcs_ids[] = { + {CCW_DEVICE(0x3088, 0x08), .driver_info = lcs_channel_type_parallel}, + {CCW_DEVICE(0x3088, 0x1f), .driver_info = lcs_channel_type_2216}, + {CCW_DEVICE(0x3088, 0x60), .driver_info = lcs_channel_type_osa2}, + {}, +}; +MODULE_DEVICE_TABLE(ccw, lcs_ids); + +static struct ccw_driver lcs_ccw_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "lcs", + }, + .ids = lcs_ids, + .probe = ccwgroup_probe_ccwdev, + .remove = ccwgroup_remove_ccwdev, + .int_class = IRQIO_LCS, +}; + /** * LCS ccwgroup driver registration */ static struct ccwgroup_driver lcs_group_driver = { - .owner = THIS_MODULE, - .name = "lcs", - .max_slaves = 2, - .driver_id = 0xD3C3E2, - .probe = lcs_probe_device, + .driver = { + .owner = THIS_MODULE, + .name = "lcs", + }, + .setup = lcs_probe_device, .remove = lcs_remove_device, .set_online = lcs_new_device, .set_offline = lcs_shutdown_device, + .prepare = lcs_prepare, + .complete = lcs_complete, + .freeze = lcs_freeze, + .thaw = lcs_thaw, + .restore = lcs_restore, +}; + +static ssize_t lcs_driver_group_store(struct device_driver *ddrv, + const char *buf, size_t count) +{ + int err; + err = ccwgroup_create_dev(lcs_root_dev, &lcs_group_driver, 2, buf); + return err ? err : count; +} +static DRIVER_ATTR(group, 0200, NULL, lcs_driver_group_store); + +static struct attribute *lcs_drv_attrs[] = { + &driver_attr_group.attr, + NULL, +}; +static struct attribute_group lcs_drv_attr_group = { + .attrs = lcs_drv_attrs, +}; +static const struct attribute_group *lcs_drv_attr_groups[] = { + &lcs_drv_attr_group, + NULL, }; /** @@ -2304,20 +2441,33 @@ __init lcs_init_module(void) { int rc; - PRINT_INFO("Loading %s\n",version); + pr_info("Loading %s\n", version); rc = lcs_register_debug_facility(); LCS_DBF_TEXT(0, setup, "lcsinit"); - if (rc) { - PRINT_ERR("Initialization failed\n"); - return rc; - } - - rc = register_cu3088_discipline(&lcs_group_driver); - if (rc) { - PRINT_ERR("Initialization failed\n"); - return rc; - } + if (rc) + goto out_err; + lcs_root_dev = root_device_register("lcs"); + rc = PTR_ERR_OR_ZERO(lcs_root_dev); + if (rc) + goto register_err; + rc = ccw_driver_register(&lcs_ccw_driver); + if (rc) + goto ccw_err; + lcs_group_driver.driver.groups = lcs_drv_attr_groups; + rc = ccwgroup_driver_register(&lcs_group_driver); + if (rc) + goto ccwgroup_err; return 0; + +ccwgroup_err: + ccw_driver_unregister(&lcs_ccw_driver); +ccw_err: + root_device_unregister(lcs_root_dev); +register_err: + lcs_unregister_debug_facility(); +out_err: + pr_err("Initializing the lcs device driver failed\n"); + return rc; } @@ -2327,9 +2477,11 @@ __init lcs_init_module(void) static void __exit lcs_cleanup_module(void) { - PRINT_INFO("Terminating lcs module.\n"); + pr_info("Terminating lcs module.\n"); LCS_DBF_TEXT(0, trace, "cleanup"); - unregister_cu3088_discipline(&lcs_group_driver); + ccwgroup_driver_unregister(&lcs_group_driver); + ccw_driver_unregister(&lcs_ccw_driver); + root_device_unregister(lcs_root_dev); lcs_unregister_debug_facility(); } |
