diff options
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/Makefile | 5 | ||||
-rw-r--r-- | drivers/net/caif/caif_serial.c | 1 | ||||
-rw-r--r-- | drivers/net/caif/caif_shmcore.c | 52 | ||||
-rw-r--r-- | drivers/net/ethernet/smsc/smsc911x.c | 51 | ||||
-rw-r--r-- | drivers/net/m6718_modem_net.c | 333 | ||||
-rw-r--r-- | drivers/net/u8500_shrm.c | 318 |
6 files changed, 745 insertions, 15 deletions
diff --git a/drivers/net/Makefile b/drivers/net/Makefile index a6b8ce11a22..a0f2484368b 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -70,3 +70,8 @@ obj-$(CONFIG_USB_IPHETH) += usb/ obj-$(CONFIG_USB_CDC_PHONET) += usb/ obj-$(CONFIG_HYPERV_NET) += hyperv/ + +ifdef CONFIG_PHONET +obj-$(CONFIG_U8500_SHRM) += u8500_shrm.o +obj-$(CONFIG_MODEM_M6718_SPI) += m6718_modem_net.o +endif diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c index 8a3054b8481..957363ceae4 100644 --- a/drivers/net/caif/caif_serial.c +++ b/drivers/net/caif/caif_serial.c @@ -182,6 +182,7 @@ static void ldisc_receive(struct tty_struct *tty, const u8 *data, * This is not yet handled. */ + BUG_ON(ser->dev == NULL); /* * Workaround for garbage at start of transmission, diff --git a/drivers/net/caif/caif_shmcore.c b/drivers/net/caif/caif_shmcore.c index 5b2041319a3..fc55bf65f1c 100644 --- a/drivers/net/caif/caif_shmcore.c +++ b/drivers/net/caif/caif_shmcore.c @@ -13,6 +13,7 @@ #include <linux/list.h> #include <linux/netdevice.h> #include <linux/if_arp.h> +#include <linux/kthread.h> #include <net/caif/caif_device.h> #include <net/caif/caif_shm.h> @@ -107,8 +108,13 @@ struct shmdrv_layer { struct workqueue_struct *pshm_tx_workqueue; struct workqueue_struct *pshm_rx_workqueue; + struct kthread_worker pshm_flow_ctrl_kw; + struct task_struct *pshm_flow_ctrl_kw_task; + struct work_struct shm_tx_work; struct work_struct shm_rx_work; + struct kthread_work shm_flow_on_work; + struct kthread_work shm_flow_off_work; struct sk_buff_head sk_qhead; struct shmdev_layer *pshm_dev; @@ -126,6 +132,24 @@ static int shm_netdev_close(struct net_device *shm_netdev) return 0; } +static void shm_flow_on_work_func(struct kthread_work *work) +{ + struct shmdrv_layer *pshm_drv = container_of(work, struct shmdrv_layer, shm_flow_on_work); + + pshm_drv->cfdev.flowctrl + (pshm_drv->pshm_dev->pshm_netdev, + CAIF_FLOW_ON); +} + +static void shm_flow_off_work_func(struct kthread_work *work) +{ + struct shmdrv_layer *pshm_drv = container_of(work, struct shmdrv_layer, shm_flow_off_work); + + pshm_drv->cfdev.flowctrl + (pshm_drv->pshm_dev->pshm_netdev, + CAIF_FLOW_OFF); +} + int caif_shmdrv_rx_cb(u32 mbx_msg, void *priv) { struct buf_list *pbuf; @@ -238,11 +262,9 @@ int caif_shmdrv_rx_cb(u32 mbx_msg, void *priv) if ((avail_emptybuff > HIGH_WATERMARK) && (!pshm_drv->tx_empty_available)) { pshm_drv->tx_empty_available = 1; + queue_kthread_work(&pshm_drv->pshm_flow_ctrl_kw, + &pshm_drv->shm_flow_on_work); spin_unlock_irqrestore(&pshm_drv->lock, flags); - pshm_drv->cfdev.flowctrl - (pshm_drv->pshm_dev->pshm_netdev, - CAIF_FLOW_ON); - /* Schedule the work queue. if required */ if (!work_pending(&pshm_drv->shm_tx_work)) @@ -426,11 +448,8 @@ static void shm_tx_work_func(struct work_struct *tx_work) pshm_drv->tx_empty_available) { /* Update blocking condition. */ pshm_drv->tx_empty_available = 0; - spin_unlock_irqrestore(&pshm_drv->lock, flags); - pshm_drv->cfdev.flowctrl - (pshm_drv->pshm_dev->pshm_netdev, - CAIF_FLOW_OFF); - spin_lock_irqsave(&pshm_drv->lock, flags); + queue_kthread_work(&pshm_drv->pshm_flow_ctrl_kw, + &pshm_drv->shm_flow_off_work); } /* * We simply return back to the caller if we do not have space @@ -503,7 +522,8 @@ static void shm_tx_work_func(struct work_struct *tx_work) pbuf->frames++; pbuf->frm_ofs += frmlen + (frmlen % 32); - } while (pbuf->frames < SHM_MAX_FRMS_PER_BUF); + } while (pbuf->frames < SHM_MAX_FRMS_PER_BUF && + pbuf->frm_ofs < pbuf->len); /* Assign buffer as full. */ list_add_tail(&pbuf->list, &pshm_drv->tx_full_list); @@ -562,6 +582,7 @@ int caif_shmcore_probe(struct shmdev_layer *pshm_dev) { int result, j; struct shmdrv_layer *pshm_drv = NULL; + struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; pshm_dev->pshm_netdev = alloc_netdev(sizeof(struct shmdrv_layer), "cfshm%d", shm_netdev_setup); @@ -622,11 +643,20 @@ int caif_shmcore_probe(struct shmdev_layer *pshm_dev) INIT_WORK(&pshm_drv->shm_tx_work, shm_tx_work_func); INIT_WORK(&pshm_drv->shm_rx_work, shm_rx_work_func); + init_kthread_work(&pshm_drv->shm_flow_on_work, shm_flow_on_work_func); + init_kthread_work(&pshm_drv->shm_flow_off_work, shm_flow_off_work_func); + pshm_drv->pshm_tx_workqueue = create_singlethread_workqueue("shm_tx_work"); pshm_drv->pshm_rx_workqueue = create_singlethread_workqueue("shm_rx_work"); + init_kthread_worker(&pshm_drv->pshm_flow_ctrl_kw); + pshm_drv->pshm_flow_ctrl_kw_task = kthread_run(kthread_worker_fn, + &pshm_drv->pshm_flow_ctrl_kw, "pshm_caif_flow_ctrl"); + /* must use the FIFO scheduler as it is realtime sensitive */ + sched_setscheduler(pshm_drv->pshm_flow_ctrl_kw_task, SCHED_FIFO, ¶m); + for (j = 0; j < NR_TX_BUF; j++) { struct buf_list *tx_buf = kmalloc(sizeof(struct buf_list), GFP_KERNEL); @@ -744,6 +774,8 @@ void caif_shmcore_remove(struct net_device *pshm_netdev) /* Destroy work queues. */ destroy_workqueue(pshm_drv->pshm_tx_workqueue); destroy_workqueue(pshm_drv->pshm_rx_workqueue); + flush_kthread_worker(&pshm_drv->pshm_flow_ctrl_kw); + kthread_stop(pshm_drv->pshm_flow_ctrl_kw_task); unregister_netdev(pshm_netdev); } diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c index 24d2df068d7..3b2a64ab1d6 100644 --- a/drivers/net/ethernet/smsc/smsc911x.c +++ b/drivers/net/ethernet/smsc/smsc911x.c @@ -33,6 +33,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/crc32.h> +#include <linux/clk.h> #include <linux/delay.h> #include <linux/errno.h> #include <linux/etherdevice.h> @@ -144,6 +145,9 @@ struct smsc911x_data { /* regulators */ struct regulator_bulk_data supplies[SMSC911X_NUM_SUPPLIES]; + + /* clock */ + struct clk *fsmc_clk; }; /* Easy access to information */ @@ -369,7 +373,7 @@ out: } /* - * enable resources, currently just regulators. + * enable resources, regulators & clocks. */ static int smsc911x_enable_resources(struct platform_device *pdev) { @@ -379,9 +383,17 @@ static int smsc911x_enable_resources(struct platform_device *pdev) ret = regulator_bulk_enable(ARRAY_SIZE(pdata->supplies), pdata->supplies); - if (ret) + if (ret) { netdev_err(ndev, "failed to enable regulators %d\n", ret); + return ret; + } + + if (pdata->fsmc_clk) { + ret = clk_enable(pdata->fsmc_clk); + if (ret < 0) + netdev_err(ndev, "failed to enable clock %d\n", ret); + } return ret; } @@ -396,6 +408,8 @@ static int smsc911x_disable_resources(struct platform_device *pdev) ret = regulator_bulk_disable(ARRAY_SIZE(pdata->supplies), pdata->supplies); + if (pdata->fsmc_clk) + clk_disable(pdata->fsmc_clk); return ret; } @@ -418,9 +432,17 @@ static int smsc911x_request_resources(struct platform_device *pdev) ret = regulator_bulk_get(&pdev->dev, ARRAY_SIZE(pdata->supplies), pdata->supplies); - if (ret) - netdev_err(ndev, "couldn't get regulators %d\n", - ret); + if (ret) { + netdev_err(ndev, "couldn't get regulators %d\n", ret); + return ret; + } + + /* Request clock, ignore if not here */ + pdata->fsmc_clk = clk_get(NULL, "fsmc"); + if (IS_ERR(pdata->fsmc_clk)) { + netdev_warn(ndev, "couldn't get clock %d\n", ret); + pdata->fsmc_clk = NULL; + } return ret; } @@ -436,6 +458,12 @@ static void smsc911x_free_resources(struct platform_device *pdev) /* Free regulators */ regulator_bulk_free(ARRAY_SIZE(pdata->supplies), pdata->supplies); + + /* Free clock */ + if (pdata->fsmc_clk) { + clk_put(pdata->fsmc_clk); + pdata->fsmc_clk = NULL; + } } /* waits for MAC not busy, with timeout. Only called by smsc911x_mac_read @@ -2346,6 +2374,7 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev) unsigned int intcfg = 0; int res_size, irq_flags; int retval; + int to = 100; pr_info("Driver version %s\n", SMSC_DRV_VERSION); @@ -2424,6 +2453,18 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev) if (pdata->config.shift) pdata->ops = &shifted_smsc911x_ops; + /* poll the READY bit in PMT_CTRL. Any other access to the device is + * forbidden while this bit isn't set. Try for 100ms + */ + while (!(smsc911x_reg_read(pdata, PMT_CTRL) & PMT_CTRL_READY_) && --to) + udelay(1000); + + if (to == 0) { + pr_err("Device not READY in 100ms aborting\n"); + goto out_0; + } + + retval = smsc911x_init(dev); if (retval < 0) goto out_disable_resources; diff --git a/drivers/net/m6718_modem_net.c b/drivers/net/m6718_modem_net.c new file mode 100644 index 00000000000..f64a775560b --- /dev/null +++ b/drivers/net/m6718_modem_net.c @@ -0,0 +1,333 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Chris Blair <chris.blair@stericsson.com> for ST-Ericsson + * based on u8500_shrm.c + * + * License terms: GNU General Public License (GPL) version 2 + * + * M6718 modem net device interface. + */ +#include <linux/if_ether.h> +#include <linux/netdevice.h> +#include <linux/phonet.h> +#include <linux/if_phonet.h> +#include <linux/if_arp.h> +#include <net/sock.h> +#include <net/phonet/phonet.h> +#include <net/phonet/pep.h> +#include <linux/modem/m6718_spi/modem_net.h> +#include <linux/modem/m6718_spi/modem_driver.h> +#include <linux/modem/m6718_spi/modem_char.h> +#include <linux/ratelimit.h> + + +/** + * modem_net_receive() - receive data and copy to user space buffer + * @dev: pointer to the network device structure + * + * Copy data from ISI queue to the user space buffer. + */ +int modem_net_receive(struct net_device *dev) +{ + struct sk_buff *skb; + struct isa_device_context *isadev; + struct message_queue *q; + u32 msgsize; + u32 size = 0; + struct modem_spi_net_dev *net_iface_priv = + (struct modem_spi_net_dev *)netdev_priv(dev); + struct modem_spi_dev *modem_spi_dev = net_iface_priv->modem_spi_dev; + + isadev = &modem_spi_dev->isa_context->isadev[MODEM_M6718_SPI_CHN_ISI]; + q = &isadev->dl_queue; + + spin_lock_bh(&q->update_lock); + if (list_empty(&q->msg_list)) { + spin_unlock_bh(&q->update_lock); + dev_dbg(modem_spi_dev->dev, "empty queue!\n"); + return 0; + } + spin_unlock_bh(&q->update_lock); + + msgsize = modem_isa_msg_size(q); + if (msgsize <= 0) + return msgsize; + + /* + * The packet has been retrieved from the transmission + * medium. Build an skb around it, so upper layers can handle it + */ + skb = dev_alloc_skb(msgsize); + if (!skb) { + pr_notice_ratelimited("isa rx: low on mem - packet dropped\n"); + dev->stats.rx_dropped++; + return -ENOMEM; + } + + if ((q->readptr + msgsize) >= q->size) { + size = (q->size - q->readptr); + /* copy first part of msg */ + skb_copy_to_linear_data(skb, + (u8 *)(q->fifo_base + q->readptr), size); + skb_put(skb, size); + + /* copy second part of msg at the top of fifo */ + skb_copy_to_linear_data_offset(skb, size, + (u8 *)(q->fifo_base), (msgsize - size)); + skb_put(skb, msgsize - size); + + } else { + skb_copy_to_linear_data(skb, + (u8 *)(q->fifo_base + q->readptr), msgsize); + skb_put(skb, msgsize); + } + + spin_lock_bh(&q->update_lock); + modem_isa_unqueue_msg(q); + spin_unlock_bh(&q->update_lock); + + skb_reset_mac_header(skb); + __skb_pull(skb, dev->hard_header_len); + /* write metadata and then pass to the receive level */ + skb->dev = dev; + skb->protocol = htons(ETH_P_PHONET); + skb->priority = 0; + skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */ + if (likely(netif_rx_ni(skb) == NET_RX_SUCCESS)) { + dev->stats.rx_packets++; + dev->stats.rx_bytes += msgsize; + } else { + dev->stats.rx_dropped++; + } + + return msgsize; +} +EXPORT_SYMBOL_GPL(modem_net_receive); + +static int netdev_isa_open(struct net_device *dev) +{ + struct modem_spi_net_dev *net_iface_priv = + (struct modem_spi_net_dev *)netdev_priv(dev); + struct modem_spi_dev *modem_spi_dev = net_iface_priv->modem_spi_dev; + + modem_spi_dev->netdev_flag_up = 1; + if (!netif_carrier_ok(dev)) + netif_carrier_on(dev); + netif_wake_queue(dev); + return 0; +} + +static int netdev_isa_close(struct net_device *dev) +{ + struct modem_spi_net_dev *net_iface_priv = + (struct modem_spi_net_dev *)netdev_priv(dev); + struct modem_spi_dev *modem_spi_dev = net_iface_priv->modem_spi_dev; + + modem_spi_dev->netdev_flag_up = 0; + netif_stop_queue(dev); + netif_carrier_off(dev); + return 0; +} + +static int netdev_isa_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct if_phonet_req *req = (struct if_phonet_req *)ifr; + + switch (cmd) { + case SIOCPNGAUTOCONF: + req->ifr_phonet_autoconf.device = PN_DEV_HOST; + return 0; + } + return -ENOIOCTLCMD; +} + +static struct net_device_stats *netdev_isa_stats(struct net_device *dev) +{ + return &dev->stats; +} + +/** + * netdev_isa_write() - write through the net interface + * @skb: pointer to the socket buffer + * @dev: pointer to the network device structure + * + * Copies data(ISI message) from the user buffer to the kernel buffer and + * schedule transfer thread to transmit the message to the modem via FIFO. + */ +static netdev_tx_t netdev_isa_write(struct sk_buff *skb, struct net_device *dev) +{ + int err; + int retval = 0; + struct modem_spi_net_dev *net_iface_priv = + (struct modem_spi_net_dev *)netdev_priv(dev); + struct modem_spi_dev *modem_spi_dev = net_iface_priv->modem_spi_dev; + + /* + * FIXME: + * U8500 modem requires that Pipe created/enabled Indication should + * be sent from the port corresponding to GPRS socket. + * Also, the U8500 modem does not implement Pipe controller + * which takes care of port manipulations for GPRS traffic. + * + * Now, APE has GPRS socket and the socket for sending + * Indication msgs bound to different ports. + * Phonet stack does not allow an indication msg to be sent + * from GPRS socket, since Phonet stack assumes the presence + * of Pipe controller in modem. + * + * So, due to lack of Pipe controller implementation in the + * U8500 modem, carry out the port manipulation related to + * GPRS traffic here. + * Ideally, it should be done either by Pipe controller in + * modem OR some implementation of Pipe controller on APE side + */ + if (skb->data[RESOURCE_ID_INDEX] == PN_PIPE) { + if ((skb->data[MSG_ID_INDEX] == PNS_PIPE_CREATED_IND) || + (skb->data[MSG_ID_INDEX] == PNS_PIPE_ENABLED_IND) || + (skb->data[MSG_ID_INDEX] == PNS_PIPE_DISABLED_IND)) + skb->data[SRC_OBJ_INDEX] = skb->data[PIPE_HDL_INDEX]; + } + + spin_lock_bh(&modem_spi_dev->isa_context->common_tx_lock); + err = modem_m6718_spi_send(modem_spi_dev, MODEM_M6718_SPI_CHN_ISI, + skb->len, skb->data); + if (!err) { + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + retval = NETDEV_TX_OK; + dev_kfree_skb(skb); + } else { + dev->stats.tx_dropped++; + retval = NETDEV_TX_BUSY; + } + spin_unlock_bh(&modem_spi_dev->isa_context->common_tx_lock); + + return retval; +} + +static const struct net_device_ops modem_netdev_ops = { + .ndo_open = netdev_isa_open, + .ndo_stop = netdev_isa_close, + .ndo_do_ioctl = netdev_isa_ioctl, + .ndo_start_xmit = netdev_isa_write, + .ndo_get_stats = netdev_isa_stats, +}; + +static void net_device_init(struct net_device *dev) +{ + struct modem_spi_net_dev *net_iface_priv; + + dev->netdev_ops = &modem_netdev_ops; + dev->header_ops = &phonet_header_ops; + dev->type = ARPHRD_PHONET; + dev->flags = IFF_POINTOPOINT | IFF_NOARP; + dev->mtu = PHONET_MAX_MTU; + dev->hard_header_len = MODEM_HLEN; + dev->addr_len = PHONET_ALEN; + dev->tx_queue_len = PN_TX_QUEUE_LEN; + dev->destructor = free_netdev; + dev->dev_addr[0] = PN_LINK_ADDR; + net_iface_priv = netdev_priv(dev); + memset(net_iface_priv, 0 , sizeof(struct modem_spi_net_dev)); +} + +int modem_net_init(struct modem_spi_dev *modem_spi_dev) +{ + struct net_device *nw_device; + struct modem_spi_net_dev *net_iface_priv; + int err; + /* + * keep the same net device name as U8500 to allow userspace clients + * to remain unchanged and use the same interfaces + */ + char *devname = "shrm%d"; + + /* allocate the net device */ + nw_device = modem_spi_dev->ndev = + alloc_netdev(sizeof(struct modem_spi_net_dev), + devname, net_device_init); + if (nw_device == NULL) { + dev_err(modem_spi_dev->dev, + "failed to allocate modem net device\n"); + return -ENOMEM; + } + err = register_netdev(modem_spi_dev->ndev); + if (err) { + dev_err(modem_spi_dev->dev, + "failed to register modem net device: error %d\n", err); + free_netdev(modem_spi_dev->ndev); + return -ENODEV; + } + dev_dbg(modem_spi_dev->dev, "registered modem net device\n"); + + net_iface_priv = (struct modem_spi_net_dev *)netdev_priv(nw_device); + net_iface_priv->modem_spi_dev = modem_spi_dev; + net_iface_priv->iface_num = 0; + return err; +} +EXPORT_SYMBOL_GPL(modem_net_init); + +int modem_net_stop(struct net_device *dev) +{ + netif_stop_queue(dev); + return 0; +} + +int modem_net_restart(struct net_device *dev) +{ + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + return 0; +} +EXPORT_SYMBOL_GPL(modem_net_restart); + +int modem_net_start(struct net_device *dev) +{ + struct modem_spi_net_dev *net_iface_priv = + (struct modem_spi_net_dev *)netdev_priv(dev); + struct modem_spi_dev *modem_spi_dev = net_iface_priv->modem_spi_dev; + + if (!netif_carrier_ok(dev)) + netif_carrier_on(dev); + netif_start_queue(dev); + modem_spi_dev->netdev_flag_up = 1; + return 0; +} +EXPORT_SYMBOL_GPL(modem_net_start); + +int modem_net_suspend(struct net_device *dev) +{ + if (netif_running(dev)) { + netif_stop_queue(dev); + netif_carrier_off(dev); + } + netif_device_detach(dev); + return 0; +} +EXPORT_SYMBOL_GPL(modem_net_suspend); + +int modem_net_resume(struct net_device *dev) +{ + netif_device_attach(dev); + if (netif_running(dev)) { + netif_carrier_on(dev); + netif_wake_queue(dev); + } + return 0; +} +EXPORT_SYMBOL_GPL(modem_net_resume); + +void modem_net_exit(struct modem_spi_dev *modem_spi_dev) +{ + if (modem_spi_dev && modem_spi_dev->ndev) { + unregister_netdev(modem_spi_dev->ndev); + modem_spi_dev->ndev = NULL; + dev_dbg(modem_spi_dev->dev, "removed modem net device\n"); + } +} +EXPORT_SYMBOL_GPL(modem_net_exit); + +MODULE_AUTHOR("Chris Blair <chris.blair@stericsson.com>"); +MODULE_DESCRIPTION("M6718 modem IPC net device interface"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/u8500_shrm.c b/drivers/net/u8500_shrm.c new file mode 100644 index 00000000000..0e813bbb3cc --- /dev/null +++ b/drivers/net/u8500_shrm.c @@ -0,0 +1,318 @@ +/* + * Copyright (C) ST-Ericsson SA 2009 + * + * Author: Biju Das <biju.das@stericsson.com> for ST-Ericsson + * Author: Kumar Sanghvi <kumar.sanghvi@stericsson.com> for ST-Ericsson + * Author: Arun Murthy <arun.murthy@stericsson.com> for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/if_ether.h> +#include <linux/netdevice.h> +#include <linux/phonet.h> +#include <linux/if_phonet.h> +#include <linux/if_arp.h> +#include <linux/modem/shrm/shrm_driver.h> +#include <linux/modem/shrm/shrm_private.h> +#include <linux/modem/shrm/shrm_config.h> +#include <linux/modem/shrm/shrm_net.h> +#include <linux/modem/shrm/shrm.h> +#include <net/sock.h> +#include <net/phonet/phonet.h> +#include <net/phonet/pep.h> + + +/** + * shrm_net_receive() - receive data and copy to user space buffer + * @dev: pointer to the network device structure + * + * Copy data from ISI queue to the user space buffer. + */ +int shrm_net_receive(struct net_device *dev) +{ + struct sk_buff *skb; + struct isadev_context *isadev; + struct message_queue *q; + u32 msgsize; + u32 size = 0; + struct shrm_net_iface_priv *net_iface_priv = + (struct shrm_net_iface_priv *)netdev_priv(dev); + struct shrm_dev *shrm = net_iface_priv->shrm_device; + + isadev = &shrm->isa_context->isadev[ISI_MESSAGING]; + q = &isadev->dl_queue; + + spin_lock_bh(&q->update_lock); + if (list_empty(&q->msg_list)) { + spin_unlock_bh(&q->update_lock); + dev_dbg(shrm->dev, "Empty Shrm queue\n"); + return 0; + } + spin_unlock_bh(&q->update_lock); + + msgsize = get_size_of_new_msg(q); + if (msgsize <= 0) + return msgsize; + + /* + * The packet has been retrieved from the transmission + * medium. Build an skb around it, so upper layers can handle it + */ + skb = dev_alloc_skb(msgsize); + if (!skb) { + if (printk_ratelimit()) + dev_notice(shrm->dev, + "isa rx: low on mem - packet dropped\n"); + dev->stats.rx_dropped++; + goto out; + } + + if ((q->readptr+msgsize) >= q->size) { + size = (q->size-q->readptr); + /*Copy First Part of msg*/ + skb_copy_to_linear_data(skb, + (u8 *)(q->fifo_base + q->readptr), size); + skb_put(skb, size); + + /*Copy Second Part of msg at the top of fifo*/ + skb_copy_to_linear_data_offset(skb, size, + (u8 *)(q->fifo_base), (msgsize - size)); + skb_put(skb, msgsize-size); + + } else { + skb_copy_to_linear_data(skb, + (u8 *)(q->fifo_base+q->readptr), msgsize); + skb_put(skb, msgsize); + } + + spin_lock_bh(&q->update_lock); + remove_msg_from_queue(q); + spin_unlock_bh(&q->update_lock); + + skb_reset_mac_header(skb); + __skb_pull(skb, dev->hard_header_len); + /*Write metadata, and then pass to the receive level*/ + skb->dev = dev;/*kmalloc(sizeof(struct net_device), GFP_ATOMIC);*/ + skb->protocol = htons(ETH_P_PHONET); + skb->priority = 0; + skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */ + if (likely(netif_rx_ni(skb) == NET_RX_SUCCESS)) { + dev->stats.rx_packets++; + dev->stats.rx_bytes += msgsize; + } else + dev->stats.rx_dropped++; + + return msgsize; +out: + return -ENOMEM; +} + +static int netdev_isa_open(struct net_device *dev) +{ + struct shrm_net_iface_priv *net_iface_priv = + (struct shrm_net_iface_priv *)netdev_priv(dev); + struct shrm_dev *shrm = net_iface_priv->shrm_device; + + shrm->netdev_flag_up = 1; + if (!netif_carrier_ok(dev)) + netif_carrier_on(dev); + netif_wake_queue(dev); + return 0; +} + +static int netdev_isa_close(struct net_device *dev) +{ + struct shrm_net_iface_priv *net_iface_priv = + (struct shrm_net_iface_priv *)netdev_priv(dev); + struct shrm_dev *shrm = net_iface_priv->shrm_device; + + shrm->netdev_flag_up = 0; + netif_stop_queue(dev); + netif_carrier_off(dev); + return 0; +} + +static int netdev_isa_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct if_phonet_req *req = (struct if_phonet_req *)ifr; + + switch (cmd) { + case SIOCPNGAUTOCONF: + req->ifr_phonet_autoconf.device = PN_DEV_HOST; + return 0; + } + return -ENOIOCTLCMD; +} + +static struct net_device_stats *netdev_isa_stats(struct net_device *dev) +{ + return &dev->stats; +} + +/** + * netdev_isa_write() - write through the net interface + * @skb: pointer to the socket buffer + * @dev: pointer to the network device structure + * + * Copies data(ISI message) from the user buffer to the kernel buffer and + * schedule transfer thread to transmit the message to the modem via FIFO. + */ +static netdev_tx_t netdev_isa_write(struct sk_buff *skb, struct net_device *dev) +{ + int err; + int retval = 0; + struct shrm_net_iface_priv *net_iface_priv = + (struct shrm_net_iface_priv *)netdev_priv(dev); + struct shrm_dev *shrm = net_iface_priv->shrm_device; + + /* + * FIXME: + * U8500 modem requires that Pipe created/enabled Indication should + * be sent from the port corresponding to GPRS socket. + * Also, the U8500 modem does not implement Pipe controller + * which takes care of port manipulations for GPRS traffic. + * + * Now, APE has GPRS socket and the socket for sending + * Indication msgs bound to different ports. + * Phonet stack does not allow an indication msg to be sent + * from GPRS socket, since Phonet stack assumes the presence + * of Pipe controller in modem. + * + * So, due to lack of Pipe controller implementation in the + * U8500 modem, carry out the port manipulation related to + * GPRS traffic here. + * Ideally, it should be done either by Pipe controller in + * modem OR some implementation of Pipe controller on APE side + */ + if (skb->data[RESOURCE_ID_INDEX] == PN_PIPE) { + if ((skb->data[MSG_ID_INDEX] == PNS_PIPE_CREATED_IND) || + (skb->data[MSG_ID_INDEX] == PNS_PIPE_ENABLED_IND) || + (skb->data[MSG_ID_INDEX] == PNS_PIPE_DISABLED_IND)) + skb->data[SRC_OBJ_INDEX] = skb->data[PIPE_HDL_INDEX]; + } + + spin_lock_bh(&shrm->isa_context->common_tx); + err = shm_write_msg(shrm, ISI_MESSAGING, skb->data, + skb->len); + if (!err) { + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + retval = NETDEV_TX_OK; + dev_kfree_skb(skb); + } else { + dev->stats.tx_dropped++; + retval = NETDEV_TX_BUSY; + } + spin_unlock_bh(&shrm->isa_context->common_tx); + + return retval; +} + +static const struct net_device_ops shrm_netdev_ops = { + .ndo_open = netdev_isa_open, + .ndo_stop = netdev_isa_close, + .ndo_do_ioctl = netdev_isa_ioctl, + .ndo_start_xmit = netdev_isa_write, + .ndo_get_stats = netdev_isa_stats, +}; + +static void shm_net_init(struct net_device *dev) +{ + struct shrm_net_iface_priv *net_iface_priv; + + dev->netdev_ops = &shrm_netdev_ops; + dev->header_ops = &phonet_header_ops; + dev->type = ARPHRD_PHONET; + dev->flags = IFF_POINTOPOINT | IFF_NOARP; + dev->mtu = PHONET_MAX_MTU; + dev->hard_header_len = SHRM_HLEN; + dev->addr_len = PHONET_ALEN; + dev->tx_queue_len = PN_TX_QUEUE_LEN; + dev->destructor = free_netdev; + dev->dev_addr[0] = PN_LINK_ADDR; + net_iface_priv = netdev_priv(dev); + memset(net_iface_priv, 0 , sizeof(struct shrm_net_iface_priv)); +} + +int shrm_register_netdev(struct shrm_dev *shrm) +{ + struct net_device *nw_device; + struct shrm_net_iface_priv *net_iface_priv; + char *devname = "shrm%d"; + int err; + + /* allocate the net device */ + nw_device = shrm->ndev = alloc_netdev( + sizeof(struct shrm_net_iface_priv), + devname, shm_net_init); + if (nw_device == NULL) { + dev_err(shrm->dev, "Failed to allocate SHRM Netdev\n"); + return -ENOMEM; + } + err = register_netdev(shrm->ndev); + if (err) { + dev_err(shrm->dev, "Err %i in reg shrm-netdev\n", err); + free_netdev(shrm->ndev); + return -ENODEV; + } + dev_info(shrm->dev, "Registered shrm netdev\n"); + + net_iface_priv = (struct shrm_net_iface_priv *)netdev_priv(nw_device); + net_iface_priv->shrm_device = shrm; + net_iface_priv->iface_num = 0; + + return err; +} + +int shrm_stop_netdev(struct net_device *dev) +{ + netif_stop_queue(dev); + return 0; +} + +int shrm_restart_netdev(struct net_device *dev) +{ + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + return 0; +} + +int shrm_start_netdev(struct net_device *dev) +{ + struct shrm_net_iface_priv *net_iface_priv = + (struct shrm_net_iface_priv *)netdev_priv(dev); + struct shrm_dev *shrm = net_iface_priv->shrm_device; + + if (!netif_carrier_ok(dev)) + netif_carrier_on(dev); + netif_start_queue(dev); + shrm->netdev_flag_up = 1; + return 0; +} + +int shrm_suspend_netdev(struct net_device *dev) +{ + if (netif_running(dev)) { + netif_stop_queue(dev); + netif_carrier_off(dev); + } + netif_device_detach(dev); + + return 0; +} + +int shrm_resume_netdev(struct net_device *dev) +{ + netif_device_attach(dev); + if (netif_running(dev)) { + netif_carrier_on(dev); + netif_wake_queue(dev); + } + + return 0; +} + +void shrm_unregister_netdev(struct shrm_dev *shrm) +{ + unregister_netdev(shrm->ndev); +} |