1b482cd20SSjur Braendeland /* 2b482cd20SSjur Braendeland * Copyright (C) ST-Ericsson AB 2010 3*26ee65e6Ssjur.brandeland@stericsson.com * Author: Sjur Brendeland 4b482cd20SSjur Braendeland * License terms: GNU General Public License (GPL) version 2 5b482cd20SSjur Braendeland */ 6b482cd20SSjur Braendeland 7b31fa5baSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ 8b31fa5baSJoe Perches 9b482cd20SSjur Braendeland #include <linux/kernel.h> 10b482cd20SSjur Braendeland #include <linux/types.h> 11b482cd20SSjur Braendeland #include <linux/errno.h> 12b482cd20SSjur Braendeland #include <linux/slab.h> 1343e36921Ssjur.brandeland@stericsson.com #include <linux/module.h> 1444764812SDmitry Tarnyagin #include <linux/pkt_sched.h> 15b482cd20SSjur Braendeland #include <net/caif/caif_layer.h> 16b482cd20SSjur Braendeland #include <net/caif/cfsrvl.h> 17b482cd20SSjur Braendeland #include <net/caif/cfpkt.h> 18b482cd20SSjur Braendeland 19b482cd20SSjur Braendeland #define SRVL_CTRL_PKT_SIZE 1 20b482cd20SSjur Braendeland #define SRVL_FLOW_OFF 0x81 21b482cd20SSjur Braendeland #define SRVL_FLOW_ON 0x80 22b482cd20SSjur Braendeland #define SRVL_SET_PIN 0x82 23b482cd20SSjur Braendeland #define SRVL_CTRL_PKT_SIZE 1 24b482cd20SSjur Braendeland 25b482cd20SSjur Braendeland #define container_obj(layr) container_of(layr, struct cfsrvl, layer) 26b482cd20SSjur Braendeland 27b482cd20SSjur Braendeland static void cfservl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, 28b482cd20SSjur Braendeland int phyid) 29b482cd20SSjur Braendeland { 30b482cd20SSjur Braendeland struct cfsrvl *service = container_obj(layr); 31b1c74247SSjur Braendeland 3243e36921Ssjur.brandeland@stericsson.com if (layr->up == NULL || layr->up->ctrlcmd == NULL) 3343e36921Ssjur.brandeland@stericsson.com return; 34b1c74247SSjur Braendeland 35b482cd20SSjur Braendeland switch (ctrl) { 36b482cd20SSjur Braendeland case CAIF_CTRLCMD_INIT_RSP: 37b482cd20SSjur Braendeland service->open = true; 38b482cd20SSjur Braendeland layr->up->ctrlcmd(layr->up, ctrl, phyid); 39b482cd20SSjur Braendeland break; 40b482cd20SSjur Braendeland case CAIF_CTRLCMD_DEINIT_RSP: 41b482cd20SSjur Braendeland case CAIF_CTRLCMD_INIT_FAIL_RSP: 42b482cd20SSjur Braendeland service->open = false; 43b482cd20SSjur Braendeland layr->up->ctrlcmd(layr->up, ctrl, phyid); 44b482cd20SSjur Braendeland break; 45b482cd20SSjur Braendeland case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND: 46b482cd20SSjur Braendeland if (phyid != service->dev_info.id) 47b482cd20SSjur Braendeland break; 48b482cd20SSjur Braendeland if (service->modem_flow_on) 49b482cd20SSjur Braendeland layr->up->ctrlcmd(layr->up, 50b482cd20SSjur Braendeland CAIF_CTRLCMD_FLOW_OFF_IND, phyid); 51b482cd20SSjur Braendeland service->phy_flow_on = false; 52b482cd20SSjur Braendeland break; 53b482cd20SSjur Braendeland case _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND: 54b482cd20SSjur Braendeland if (phyid != service->dev_info.id) 55b482cd20SSjur Braendeland return; 56b482cd20SSjur Braendeland if (service->modem_flow_on) { 57b482cd20SSjur Braendeland layr->up->ctrlcmd(layr->up, 58b482cd20SSjur Braendeland CAIF_CTRLCMD_FLOW_ON_IND, 59b482cd20SSjur Braendeland phyid); 60b482cd20SSjur Braendeland } 61b482cd20SSjur Braendeland service->phy_flow_on = true; 62b482cd20SSjur Braendeland break; 63b482cd20SSjur Braendeland case CAIF_CTRLCMD_FLOW_OFF_IND: 64b482cd20SSjur Braendeland if (service->phy_flow_on) { 65b482cd20SSjur Braendeland layr->up->ctrlcmd(layr->up, 66b482cd20SSjur Braendeland CAIF_CTRLCMD_FLOW_OFF_IND, phyid); 67b482cd20SSjur Braendeland } 68b482cd20SSjur Braendeland service->modem_flow_on = false; 69b482cd20SSjur Braendeland break; 70b482cd20SSjur Braendeland case CAIF_CTRLCMD_FLOW_ON_IND: 71b482cd20SSjur Braendeland if (service->phy_flow_on) { 72b482cd20SSjur Braendeland layr->up->ctrlcmd(layr->up, 73b482cd20SSjur Braendeland CAIF_CTRLCMD_FLOW_ON_IND, phyid); 74b482cd20SSjur Braendeland } 75b482cd20SSjur Braendeland service->modem_flow_on = true; 76b482cd20SSjur Braendeland break; 77b482cd20SSjur Braendeland case _CAIF_CTRLCMD_PHYIF_DOWN_IND: 78b482cd20SSjur Braendeland /* In case interface is down, let's fake a remove shutdown */ 79b482cd20SSjur Braendeland layr->up->ctrlcmd(layr->up, 80b482cd20SSjur Braendeland CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, phyid); 81b482cd20SSjur Braendeland break; 82b482cd20SSjur Braendeland case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND: 83b482cd20SSjur Braendeland layr->up->ctrlcmd(layr->up, ctrl, phyid); 84b482cd20SSjur Braendeland break; 85b482cd20SSjur Braendeland default: 86b31fa5baSJoe Perches pr_warn("Unexpected ctrl in cfsrvl (%d)\n", ctrl); 87b482cd20SSjur Braendeland /* We have both modem and phy flow on, send flow on */ 88b482cd20SSjur Braendeland layr->up->ctrlcmd(layr->up, ctrl, phyid); 89b482cd20SSjur Braendeland service->phy_flow_on = true; 90b482cd20SSjur Braendeland break; 91b482cd20SSjur Braendeland } 92b482cd20SSjur Braendeland } 93b482cd20SSjur Braendeland 94b482cd20SSjur Braendeland static int cfservl_modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl) 95b482cd20SSjur Braendeland { 96b482cd20SSjur Braendeland struct cfsrvl *service = container_obj(layr); 97b1c74247SSjur Braendeland 98b482cd20SSjur Braendeland caif_assert(layr != NULL); 99b482cd20SSjur Braendeland caif_assert(layr->dn != NULL); 100b482cd20SSjur Braendeland caif_assert(layr->dn->transmit != NULL); 101b1c74247SSjur Braendeland 102b1c74247SSjur Braendeland if (!service->supports_flowctrl) 103b1c74247SSjur Braendeland return 0; 104b1c74247SSjur Braendeland 105b482cd20SSjur Braendeland switch (ctrl) { 106b482cd20SSjur Braendeland case CAIF_MODEMCMD_FLOW_ON_REQ: 107b482cd20SSjur Braendeland { 108b482cd20SSjur Braendeland struct cfpkt *pkt; 109b482cd20SSjur Braendeland struct caif_payload_info *info; 110b482cd20SSjur Braendeland u8 flow_on = SRVL_FLOW_ON; 111b482cd20SSjur Braendeland pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE); 1127ac2ed0cSJoe Perches if (!pkt) 113b482cd20SSjur Braendeland return -ENOMEM; 114b482cd20SSjur Braendeland 115b482cd20SSjur Braendeland if (cfpkt_add_head(pkt, &flow_on, 1) < 0) { 116b31fa5baSJoe Perches pr_err("Packet is erroneous!\n"); 117b482cd20SSjur Braendeland cfpkt_destroy(pkt); 118b482cd20SSjur Braendeland return -EPROTO; 119b482cd20SSjur Braendeland } 120b482cd20SSjur Braendeland info = cfpkt_info(pkt); 121b482cd20SSjur Braendeland info->channel_id = service->layer.id; 122b482cd20SSjur Braendeland info->hdr_len = 1; 123b482cd20SSjur Braendeland info->dev_info = &service->dev_info; 12444764812SDmitry Tarnyagin cfpkt_set_prio(pkt, TC_PRIO_CONTROL); 125b482cd20SSjur Braendeland return layr->dn->transmit(layr->dn, pkt); 126b482cd20SSjur Braendeland } 127b482cd20SSjur Braendeland case CAIF_MODEMCMD_FLOW_OFF_REQ: 128b482cd20SSjur Braendeland { 129b482cd20SSjur Braendeland struct cfpkt *pkt; 130b482cd20SSjur Braendeland struct caif_payload_info *info; 131b482cd20SSjur Braendeland u8 flow_off = SRVL_FLOW_OFF; 132b482cd20SSjur Braendeland pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE); 1337ac2ed0cSJoe Perches if (!pkt) 134638e628aSSjur Braendeland return -ENOMEM; 135638e628aSSjur Braendeland 136b482cd20SSjur Braendeland if (cfpkt_add_head(pkt, &flow_off, 1) < 0) { 137b31fa5baSJoe Perches pr_err("Packet is erroneous!\n"); 138b482cd20SSjur Braendeland cfpkt_destroy(pkt); 139b482cd20SSjur Braendeland return -EPROTO; 140b482cd20SSjur Braendeland } 141b482cd20SSjur Braendeland info = cfpkt_info(pkt); 142b482cd20SSjur Braendeland info->channel_id = service->layer.id; 143b482cd20SSjur Braendeland info->hdr_len = 1; 144b482cd20SSjur Braendeland info->dev_info = &service->dev_info; 14544764812SDmitry Tarnyagin cfpkt_set_prio(pkt, TC_PRIO_CONTROL); 146b482cd20SSjur Braendeland return layr->dn->transmit(layr->dn, pkt); 147b482cd20SSjur Braendeland } 148b482cd20SSjur Braendeland default: 149b482cd20SSjur Braendeland break; 150b482cd20SSjur Braendeland } 151b482cd20SSjur Braendeland return -EINVAL; 152b482cd20SSjur Braendeland } 153b482cd20SSjur Braendeland 15443e36921Ssjur.brandeland@stericsson.com static void cfsrvl_release(struct cflayer *layer) 155a7da1f55SSjur Braendeland { 15643e36921Ssjur.brandeland@stericsson.com struct cfsrvl *service = container_of(layer, struct cfsrvl, layer); 157a7da1f55SSjur Braendeland kfree(service); 158a7da1f55SSjur Braendeland } 159a7da1f55SSjur Braendeland 160b482cd20SSjur Braendeland void cfsrvl_init(struct cfsrvl *service, 161b482cd20SSjur Braendeland u8 channel_id, 162b1c74247SSjur Braendeland struct dev_info *dev_info, 1633bffc475SSilviu-Mihai Popescu bool supports_flowctrl) 164b482cd20SSjur Braendeland { 165b482cd20SSjur Braendeland caif_assert(offsetof(struct cfsrvl, layer) == 0); 166b482cd20SSjur Braendeland service->open = false; 167b482cd20SSjur Braendeland service->modem_flow_on = true; 168b482cd20SSjur Braendeland service->phy_flow_on = true; 169b482cd20SSjur Braendeland service->layer.id = channel_id; 170b482cd20SSjur Braendeland service->layer.ctrlcmd = cfservl_ctrlcmd; 171b482cd20SSjur Braendeland service->layer.modemcmd = cfservl_modemcmd; 172b482cd20SSjur Braendeland service->dev_info = *dev_info; 173b1c74247SSjur Braendeland service->supports_flowctrl = supports_flowctrl; 174a7da1f55SSjur Braendeland service->release = cfsrvl_release; 1755b208656SSjur Braendeland } 1765b208656SSjur Braendeland 177b482cd20SSjur Braendeland bool cfsrvl_ready(struct cfsrvl *service, int *err) 178b482cd20SSjur Braendeland { 179b482cd20SSjur Braendeland if (!service->open) { 180b482cd20SSjur Braendeland *err = -ENOTCONN; 181b482cd20SSjur Braendeland return false; 182b482cd20SSjur Braendeland } 183374458b3SDmitry Tarnyagin return true; 184b482cd20SSjur Braendeland } 18543e36921Ssjur.brandeland@stericsson.com 186b482cd20SSjur Braendeland u8 cfsrvl_getphyid(struct cflayer *layer) 187b482cd20SSjur Braendeland { 188b482cd20SSjur Braendeland struct cfsrvl *servl = container_obj(layer); 189b482cd20SSjur Braendeland return servl->dev_info.id; 190b482cd20SSjur Braendeland } 191b482cd20SSjur Braendeland 192b482cd20SSjur Braendeland bool cfsrvl_phyid_match(struct cflayer *layer, int phyid) 193b482cd20SSjur Braendeland { 194b482cd20SSjur Braendeland struct cfsrvl *servl = container_obj(layer); 195b482cd20SSjur Braendeland return servl->dev_info.id == phyid; 196b482cd20SSjur Braendeland } 19743e36921Ssjur.brandeland@stericsson.com 19843e36921Ssjur.brandeland@stericsson.com void caif_free_client(struct cflayer *adap_layer) 19943e36921Ssjur.brandeland@stericsson.com { 20043e36921Ssjur.brandeland@stericsson.com struct cfsrvl *servl; 20143e36921Ssjur.brandeland@stericsson.com if (adap_layer == NULL || adap_layer->dn == NULL) 20243e36921Ssjur.brandeland@stericsson.com return; 20343e36921Ssjur.brandeland@stericsson.com servl = container_obj(adap_layer->dn); 20443e36921Ssjur.brandeland@stericsson.com servl->release(&servl->layer); 20543e36921Ssjur.brandeland@stericsson.com } 20643e36921Ssjur.brandeland@stericsson.com EXPORT_SYMBOL(caif_free_client); 20743e36921Ssjur.brandeland@stericsson.com 20843e36921Ssjur.brandeland@stericsson.com void caif_client_register_refcnt(struct cflayer *adapt_layer, 20943e36921Ssjur.brandeland@stericsson.com void (*hold)(struct cflayer *lyr), 21043e36921Ssjur.brandeland@stericsson.com void (*put)(struct cflayer *lyr)) 21143e36921Ssjur.brandeland@stericsson.com { 21243e36921Ssjur.brandeland@stericsson.com struct cfsrvl *service; 21343e36921Ssjur.brandeland@stericsson.com 214566f26aaSWei Yongjun if (WARN_ON(adapt_layer == NULL || adapt_layer->dn == NULL)) 215566f26aaSWei Yongjun return; 216566f26aaSWei Yongjun service = container_of(adapt_layer->dn, struct cfsrvl, layer); 21743e36921Ssjur.brandeland@stericsson.com service->hold = hold; 21843e36921Ssjur.brandeland@stericsson.com service->put = put; 21943e36921Ssjur.brandeland@stericsson.com } 22043e36921Ssjur.brandeland@stericsson.com EXPORT_SYMBOL(caif_client_register_refcnt); 221