Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  *  IBM System z PNET ID Support
0004  *
0005  *    Copyright IBM Corp. 2018
0006  */
0007 
0008 #include <linux/device.h>
0009 #include <linux/module.h>
0010 #include <linux/pci.h>
0011 #include <linux/types.h>
0012 #include <asm/ccwgroup.h>
0013 #include <asm/ccwdev.h>
0014 #include <asm/pnet.h>
0015 #include <asm/ebcdic.h>
0016 
0017 #define PNETIDS_LEN     64  /* Total utility string length in bytes
0018                      * to cover up to 4 PNETIDs of 16 bytes
0019                      * for up to 4 device ports
0020                      */
0021 #define MAX_PNETID_LEN      16  /* Max.length of a single port PNETID */
0022 #define MAX_PNETID_PORTS    (PNETIDS_LEN / MAX_PNETID_LEN)
0023                     /* Max. # of ports with a PNETID */
0024 
0025 /*
0026  * Get the PNETIDs from a device.
0027  * s390 hardware supports the definition of a so-called Physical Network
0028  * Identifier (short PNETID) per network device port. These PNETIDs can be
0029  * used to identify network devices that are attached to the same physical
0030  * network (broadcast domain).
0031  *
0032  * The device can be
0033  * - a ccwgroup device with all bundled subchannels having the same PNETID
0034  * - a PCI attached network device
0035  *
0036  * Returns:
0037  * 0:       PNETIDs extracted from device.
0038  * -ENOMEM: No memory to extract utility string.
0039  * -EOPNOTSUPP: Device type without utility string support
0040  */
0041 static int pnet_ids_by_device(struct device *dev, u8 *pnetids)
0042 {
0043     memset(pnetids, 0, PNETIDS_LEN);
0044     if (dev_is_ccwgroup(dev)) {
0045         struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
0046         u8 *util_str;
0047 
0048         util_str = ccw_device_get_util_str(gdev->cdev[0], 0);
0049         if (!util_str)
0050             return -ENOMEM;
0051         memcpy(pnetids, util_str, PNETIDS_LEN);
0052         EBCASC(pnetids, PNETIDS_LEN);
0053         kfree(util_str);
0054         return 0;
0055     }
0056     if (dev_is_pci(dev)) {
0057         struct zpci_dev *zdev = to_zpci(to_pci_dev(dev));
0058 
0059         memcpy(pnetids, zdev->util_str, sizeof(zdev->util_str));
0060         EBCASC(pnetids, sizeof(zdev->util_str));
0061         return 0;
0062     }
0063     return -EOPNOTSUPP;
0064 }
0065 
0066 /*
0067  * Extract the pnetid for a device port.
0068  *
0069  * Return 0 if a pnetid is found and -ENOENT otherwise.
0070  */
0071 int pnet_id_by_dev_port(struct device *dev, unsigned short port, u8 *pnetid)
0072 {
0073     u8 pnetids[MAX_PNETID_PORTS][MAX_PNETID_LEN];
0074     static const u8 zero[MAX_PNETID_LEN] = { 0 };
0075     int rc = 0;
0076 
0077     if (!dev || port >= MAX_PNETID_PORTS)
0078         return -ENOENT;
0079 
0080     if (!pnet_ids_by_device(dev, (u8 *)pnetids) &&
0081         memcmp(pnetids[port], zero, MAX_PNETID_LEN))
0082         memcpy(pnetid, pnetids[port], MAX_PNETID_LEN);
0083     else
0084         rc = -ENOENT;
0085 
0086     return rc;
0087 }
0088 EXPORT_SYMBOL_GPL(pnet_id_by_dev_port);
0089 
0090 MODULE_DESCRIPTION("pnetid determination from utility strings");
0091 MODULE_LICENSE("GPL");