17c657876SArnaldo Carvalho de Melo /* 27c657876SArnaldo Carvalho de Melo * net/dccp/ccid.c 37c657876SArnaldo Carvalho de Melo * 47c657876SArnaldo Carvalho de Melo * An implementation of the DCCP protocol 57c657876SArnaldo Carvalho de Melo * Arnaldo Carvalho de Melo <acme@conectiva.com.br> 67c657876SArnaldo Carvalho de Melo * 77c657876SArnaldo Carvalho de Melo * CCID infrastructure 87c657876SArnaldo Carvalho de Melo * 97c657876SArnaldo Carvalho de Melo * This program is free software; you can redistribute it and/or modify it 107c657876SArnaldo Carvalho de Melo * under the terms of the GNU General Public License version 2 as 117c657876SArnaldo Carvalho de Melo * published by the Free Software Foundation. 127c657876SArnaldo Carvalho de Melo */ 137c657876SArnaldo Carvalho de Melo 147c657876SArnaldo Carvalho de Melo #include "ccid.h" 157c657876SArnaldo Carvalho de Melo 16*ddebc973SGerrit Renker static struct ccid_operations *ccids[] = { 17*ddebc973SGerrit Renker &ccid2_ops, 18*ddebc973SGerrit Renker #ifdef CONFIG_IP_DCCP_CCID3 19*ddebc973SGerrit Renker &ccid3_ops, 20*ddebc973SGerrit Renker #endif 21*ddebc973SGerrit Renker }; 22*ddebc973SGerrit Renker 23*ddebc973SGerrit Renker static struct ccid_operations *ccid_by_number(const u8 id) 24*ddebc973SGerrit Renker { 25*ddebc973SGerrit Renker int i; 26*ddebc973SGerrit Renker 27*ddebc973SGerrit Renker for (i = 0; i < ARRAY_SIZE(ccids); i++) 28*ddebc973SGerrit Renker if (ccids[i]->ccid_id == id) 29*ddebc973SGerrit Renker return ccids[i]; 30*ddebc973SGerrit Renker return NULL; 31*ddebc973SGerrit Renker } 32*ddebc973SGerrit Renker 33*ddebc973SGerrit Renker /* check that up to @array_len members in @ccid_array are supported */ 34*ddebc973SGerrit Renker bool ccid_support_check(u8 const *ccid_array, u8 array_len) 35*ddebc973SGerrit Renker { 36*ddebc973SGerrit Renker while (array_len > 0) 37*ddebc973SGerrit Renker if (ccid_by_number(ccid_array[--array_len]) == NULL) 38*ddebc973SGerrit Renker return false; 39*ddebc973SGerrit Renker return true; 40*ddebc973SGerrit Renker } 41*ddebc973SGerrit Renker 42*ddebc973SGerrit Renker /** 43*ddebc973SGerrit Renker * ccid_get_builtin_ccids - Populate a list of built-in CCIDs 44*ddebc973SGerrit Renker * @ccid_array: pointer to copy into 45*ddebc973SGerrit Renker * @array_len: value to return length into 46*ddebc973SGerrit Renker * This function allocates memory - caller must see that it is freed after use. 47*ddebc973SGerrit Renker */ 48*ddebc973SGerrit Renker int ccid_get_builtin_ccids(u8 **ccid_array, u8 *array_len) 49*ddebc973SGerrit Renker { 50*ddebc973SGerrit Renker *ccid_array = kmalloc(ARRAY_SIZE(ccids), gfp_any()); 51*ddebc973SGerrit Renker if (*ccid_array == NULL) 52*ddebc973SGerrit Renker return -ENOBUFS; 53*ddebc973SGerrit Renker 54*ddebc973SGerrit Renker for (*array_len = 0; *array_len < ARRAY_SIZE(ccids); *array_len += 1) 55*ddebc973SGerrit Renker (*ccid_array)[*array_len] = ccids[*array_len]->ccid_id; 56*ddebc973SGerrit Renker return 0; 57*ddebc973SGerrit Renker } 58*ddebc973SGerrit Renker 59*ddebc973SGerrit Renker int ccid_getsockopt_builtin_ccids(struct sock *sk, int len, 60*ddebc973SGerrit Renker char __user *optval, int __user *optlen) 61*ddebc973SGerrit Renker { 62*ddebc973SGerrit Renker u8 *ccid_array, array_len; 63*ddebc973SGerrit Renker int err = 0; 64*ddebc973SGerrit Renker 65*ddebc973SGerrit Renker if (len < ARRAY_SIZE(ccids)) 66*ddebc973SGerrit Renker return -EINVAL; 67*ddebc973SGerrit Renker 68*ddebc973SGerrit Renker if (ccid_get_builtin_ccids(&ccid_array, &array_len)) 69*ddebc973SGerrit Renker return -ENOBUFS; 70*ddebc973SGerrit Renker 71*ddebc973SGerrit Renker if (put_user(array_len, optlen) || 72*ddebc973SGerrit Renker copy_to_user(optval, ccid_array, array_len)) 73*ddebc973SGerrit Renker err = -EFAULT; 74*ddebc973SGerrit Renker 75*ddebc973SGerrit Renker kfree(ccid_array); 76*ddebc973SGerrit Renker return err; 77*ddebc973SGerrit Renker } 78*ddebc973SGerrit Renker 79*ddebc973SGerrit Renker #ifdef ___OLD_INTERFACE_TO_BE_REMOVED___ 80d90ebcbfSGerrit Renker static u8 builtin_ccids[] = { 81d90ebcbfSGerrit Renker DCCPC_CCID2, /* CCID2 is supported by default */ 82d90ebcbfSGerrit Renker #if defined(CONFIG_IP_DCCP_CCID3) || defined(CONFIG_IP_DCCP_CCID3_MODULE) 83d90ebcbfSGerrit Renker DCCPC_CCID3, 84d90ebcbfSGerrit Renker #endif 85d90ebcbfSGerrit Renker }; 86d90ebcbfSGerrit Renker 8791f0ebf7SArnaldo Carvalho de Melo static struct ccid_operations *ccids[CCID_MAX]; 887c657876SArnaldo Carvalho de Melo #if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) 897c657876SArnaldo Carvalho de Melo static atomic_t ccids_lockct = ATOMIC_INIT(0); 907c657876SArnaldo Carvalho de Melo static DEFINE_SPINLOCK(ccids_lock); 917c657876SArnaldo Carvalho de Melo 927c657876SArnaldo Carvalho de Melo /* 937c657876SArnaldo Carvalho de Melo * The strategy is: modifications ccids vector are short, do not sleep and 947c657876SArnaldo Carvalho de Melo * veeery rare, but read access should be free of any exclusive locks. 957c657876SArnaldo Carvalho de Melo */ 967c657876SArnaldo Carvalho de Melo static void ccids_write_lock(void) 977c657876SArnaldo Carvalho de Melo { 987c657876SArnaldo Carvalho de Melo spin_lock(&ccids_lock); 997c657876SArnaldo Carvalho de Melo while (atomic_read(&ccids_lockct) != 0) { 1007c657876SArnaldo Carvalho de Melo spin_unlock(&ccids_lock); 1017c657876SArnaldo Carvalho de Melo yield(); 1027c657876SArnaldo Carvalho de Melo spin_lock(&ccids_lock); 1037c657876SArnaldo Carvalho de Melo } 1047c657876SArnaldo Carvalho de Melo } 1057c657876SArnaldo Carvalho de Melo 1067c657876SArnaldo Carvalho de Melo static inline void ccids_write_unlock(void) 1077c657876SArnaldo Carvalho de Melo { 1087c657876SArnaldo Carvalho de Melo spin_unlock(&ccids_lock); 1097c657876SArnaldo Carvalho de Melo } 1107c657876SArnaldo Carvalho de Melo 1117c657876SArnaldo Carvalho de Melo static inline void ccids_read_lock(void) 1127c657876SArnaldo Carvalho de Melo { 1137c657876SArnaldo Carvalho de Melo atomic_inc(&ccids_lockct); 114d725fdc8SOleg Nesterov smp_mb__after_atomic_inc(); 1157c657876SArnaldo Carvalho de Melo spin_unlock_wait(&ccids_lock); 1167c657876SArnaldo Carvalho de Melo } 1177c657876SArnaldo Carvalho de Melo 1187c657876SArnaldo Carvalho de Melo static inline void ccids_read_unlock(void) 1197c657876SArnaldo Carvalho de Melo { 1207c657876SArnaldo Carvalho de Melo atomic_dec(&ccids_lockct); 1217c657876SArnaldo Carvalho de Melo } 1227c657876SArnaldo Carvalho de Melo 1237c657876SArnaldo Carvalho de Melo #else 1247c657876SArnaldo Carvalho de Melo #define ccids_write_lock() do { } while(0) 1257c657876SArnaldo Carvalho de Melo #define ccids_write_unlock() do { } while(0) 1267c657876SArnaldo Carvalho de Melo #define ccids_read_lock() do { } while(0) 1277c657876SArnaldo Carvalho de Melo #define ccids_read_unlock() do { } while(0) 1287c657876SArnaldo Carvalho de Melo #endif 129*ddebc973SGerrit Renker #endif /* ___OLD_INTERFACE_TO_BE_REMOVED___ */ 1307c657876SArnaldo Carvalho de Melo 131e18b890bSChristoph Lameter static struct kmem_cache *ccid_kmem_cache_create(int obj_size, const char *fmt,...) 1327c657876SArnaldo Carvalho de Melo { 133e18b890bSChristoph Lameter struct kmem_cache *slab; 13491f0ebf7SArnaldo Carvalho de Melo char slab_name_fmt[32], *slab_name; 13591f0ebf7SArnaldo Carvalho de Melo va_list args; 13691f0ebf7SArnaldo Carvalho de Melo 13791f0ebf7SArnaldo Carvalho de Melo va_start(args, fmt); 13891f0ebf7SArnaldo Carvalho de Melo vsnprintf(slab_name_fmt, sizeof(slab_name_fmt), fmt, args); 13991f0ebf7SArnaldo Carvalho de Melo va_end(args); 14091f0ebf7SArnaldo Carvalho de Melo 14191f0ebf7SArnaldo Carvalho de Melo slab_name = kstrdup(slab_name_fmt, GFP_KERNEL); 14291f0ebf7SArnaldo Carvalho de Melo if (slab_name == NULL) 14391f0ebf7SArnaldo Carvalho de Melo return NULL; 14491f0ebf7SArnaldo Carvalho de Melo slab = kmem_cache_create(slab_name, sizeof(struct ccid) + obj_size, 0, 14520c2df83SPaul Mundt SLAB_HWCACHE_ALIGN, NULL); 14691f0ebf7SArnaldo Carvalho de Melo if (slab == NULL) 14791f0ebf7SArnaldo Carvalho de Melo kfree(slab_name); 14891f0ebf7SArnaldo Carvalho de Melo return slab; 14991f0ebf7SArnaldo Carvalho de Melo } 15091f0ebf7SArnaldo Carvalho de Melo 151e18b890bSChristoph Lameter static void ccid_kmem_cache_destroy(struct kmem_cache *slab) 15291f0ebf7SArnaldo Carvalho de Melo { 15391f0ebf7SArnaldo Carvalho de Melo if (slab != NULL) { 15491f0ebf7SArnaldo Carvalho de Melo const char *name = kmem_cache_name(slab); 15591f0ebf7SArnaldo Carvalho de Melo 15691f0ebf7SArnaldo Carvalho de Melo kmem_cache_destroy(slab); 15791f0ebf7SArnaldo Carvalho de Melo kfree(name); 15891f0ebf7SArnaldo Carvalho de Melo } 15991f0ebf7SArnaldo Carvalho de Melo } 16091f0ebf7SArnaldo Carvalho de Melo 161*ddebc973SGerrit Renker #ifdef ___OLD_INTERFACE_TO_BE_REMOVED___ 162d90ebcbfSGerrit Renker /* check that up to @array_len members in @ccid_array are supported */ 163d90ebcbfSGerrit Renker bool ccid_support_check(u8 const *ccid_array, u8 array_len) 164d90ebcbfSGerrit Renker { 165d90ebcbfSGerrit Renker u8 i, j, found; 166d90ebcbfSGerrit Renker 167d90ebcbfSGerrit Renker for (i = 0, found = 0; i < array_len; i++, found = 0) { 168d90ebcbfSGerrit Renker for (j = 0; !found && j < ARRAY_SIZE(builtin_ccids); j++) 169d90ebcbfSGerrit Renker found = (ccid_array[i] == builtin_ccids[j]); 170d90ebcbfSGerrit Renker if (!found) 171d90ebcbfSGerrit Renker return false; 172d90ebcbfSGerrit Renker } 173d90ebcbfSGerrit Renker return true; 174d90ebcbfSGerrit Renker } 175d90ebcbfSGerrit Renker 176d90ebcbfSGerrit Renker /** 177d90ebcbfSGerrit Renker * ccid_get_builtin_ccids - Provide copy of `builtin' CCID array 178d90ebcbfSGerrit Renker * @ccid_array: pointer to copy into 179d90ebcbfSGerrit Renker * @array_len: value to return length into 180d90ebcbfSGerrit Renker * This function allocates memory - caller must see that it is freed after use. 181d90ebcbfSGerrit Renker */ 182d90ebcbfSGerrit Renker int ccid_get_builtin_ccids(u8 **ccid_array, u8 *array_len) 183d90ebcbfSGerrit Renker { 184d90ebcbfSGerrit Renker *ccid_array = kmemdup(builtin_ccids, sizeof(builtin_ccids), gfp_any()); 185d90ebcbfSGerrit Renker if (*ccid_array == NULL) 186d90ebcbfSGerrit Renker return -ENOBUFS; 187d90ebcbfSGerrit Renker *array_len = ARRAY_SIZE(builtin_ccids); 188d90ebcbfSGerrit Renker return 0; 189d90ebcbfSGerrit Renker } 190d90ebcbfSGerrit Renker 191d90ebcbfSGerrit Renker int ccid_getsockopt_builtin_ccids(struct sock *sk, int len, 192d90ebcbfSGerrit Renker char __user *optval, int __user *optlen) 193d90ebcbfSGerrit Renker { 194d90ebcbfSGerrit Renker if (len < sizeof(builtin_ccids)) 195d90ebcbfSGerrit Renker return -EINVAL; 196d90ebcbfSGerrit Renker 197d90ebcbfSGerrit Renker if (put_user(sizeof(builtin_ccids), optlen) || 198d90ebcbfSGerrit Renker copy_to_user(optval, builtin_ccids, sizeof(builtin_ccids))) 199d90ebcbfSGerrit Renker return -EFAULT; 200d90ebcbfSGerrit Renker return 0; 201d90ebcbfSGerrit Renker } 202*ddebc973SGerrit Renker #endif /* ___OLD_INTERFACE_TO_BE_REMOVED___ */ 203d90ebcbfSGerrit Renker 204*ddebc973SGerrit Renker static int ccid_activate(struct ccid_operations *ccid_ops) 20591f0ebf7SArnaldo Carvalho de Melo { 20691f0ebf7SArnaldo Carvalho de Melo int err = -ENOBUFS; 20791f0ebf7SArnaldo Carvalho de Melo 20891f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_hc_rx_slab = 20991f0ebf7SArnaldo Carvalho de Melo ccid_kmem_cache_create(ccid_ops->ccid_hc_rx_obj_size, 21084a97b0aSGerrit Renker "ccid%u_hc_rx_sock", 21184a97b0aSGerrit Renker ccid_ops->ccid_id); 21291f0ebf7SArnaldo Carvalho de Melo if (ccid_ops->ccid_hc_rx_slab == NULL) 21391f0ebf7SArnaldo Carvalho de Melo goto out; 21491f0ebf7SArnaldo Carvalho de Melo 21591f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_hc_tx_slab = 21691f0ebf7SArnaldo Carvalho de Melo ccid_kmem_cache_create(ccid_ops->ccid_hc_tx_obj_size, 21784a97b0aSGerrit Renker "ccid%u_hc_tx_sock", 21884a97b0aSGerrit Renker ccid_ops->ccid_id); 21991f0ebf7SArnaldo Carvalho de Melo if (ccid_ops->ccid_hc_tx_slab == NULL) 22091f0ebf7SArnaldo Carvalho de Melo goto out_free_rx_slab; 2217c657876SArnaldo Carvalho de Melo 222*ddebc973SGerrit Renker pr_info("CCID: Activated CCID %d (%s)\n", 22391f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_id, ccid_ops->ccid_name); 224*ddebc973SGerrit Renker err = 0; 22591f0ebf7SArnaldo Carvalho de Melo out: 2267c657876SArnaldo Carvalho de Melo return err; 22791f0ebf7SArnaldo Carvalho de Melo out_free_rx_slab: 22891f0ebf7SArnaldo Carvalho de Melo ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab); 22991f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_hc_rx_slab = NULL; 23091f0ebf7SArnaldo Carvalho de Melo goto out; 2317c657876SArnaldo Carvalho de Melo } 2327c657876SArnaldo Carvalho de Melo 233*ddebc973SGerrit Renker static void ccid_deactivate(struct ccid_operations *ccid_ops) 2347c657876SArnaldo Carvalho de Melo { 23591f0ebf7SArnaldo Carvalho de Melo ccid_kmem_cache_destroy(ccid_ops->ccid_hc_tx_slab); 23691f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_hc_tx_slab = NULL; 23791f0ebf7SArnaldo Carvalho de Melo ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab); 23891f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_hc_rx_slab = NULL; 23991f0ebf7SArnaldo Carvalho de Melo 240*ddebc973SGerrit Renker pr_info("CCID: Deactivated CCID %d (%s)\n", 24191f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_id, ccid_ops->ccid_name); 2427c657876SArnaldo Carvalho de Melo } 2437c657876SArnaldo Carvalho de Melo 24491f0ebf7SArnaldo Carvalho de Melo struct ccid *ccid_new(unsigned char id, struct sock *sk, int rx, gfp_t gfp) 2457c657876SArnaldo Carvalho de Melo { 246*ddebc973SGerrit Renker struct ccid_operations *ccid_ops = ccid_by_number(id); 24791f0ebf7SArnaldo Carvalho de Melo struct ccid *ccid = NULL; 2487c657876SArnaldo Carvalho de Melo 24991f0ebf7SArnaldo Carvalho de Melo if (ccid_ops == NULL) 250*ddebc973SGerrit Renker goto out; 25191f0ebf7SArnaldo Carvalho de Melo 25291f0ebf7SArnaldo Carvalho de Melo ccid = kmem_cache_alloc(rx ? ccid_ops->ccid_hc_rx_slab : 25391f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_hc_tx_slab, gfp); 25491f0ebf7SArnaldo Carvalho de Melo if (ccid == NULL) 255*ddebc973SGerrit Renker goto out; 25691f0ebf7SArnaldo Carvalho de Melo ccid->ccid_ops = ccid_ops; 25791f0ebf7SArnaldo Carvalho de Melo if (rx) { 25891f0ebf7SArnaldo Carvalho de Melo memset(ccid + 1, 0, ccid_ops->ccid_hc_rx_obj_size); 25991f0ebf7SArnaldo Carvalho de Melo if (ccid->ccid_ops->ccid_hc_rx_init != NULL && 26091f0ebf7SArnaldo Carvalho de Melo ccid->ccid_ops->ccid_hc_rx_init(ccid, sk) != 0) 26191f0ebf7SArnaldo Carvalho de Melo goto out_free_ccid; 26291f0ebf7SArnaldo Carvalho de Melo } else { 26391f0ebf7SArnaldo Carvalho de Melo memset(ccid + 1, 0, ccid_ops->ccid_hc_tx_obj_size); 26491f0ebf7SArnaldo Carvalho de Melo if (ccid->ccid_ops->ccid_hc_tx_init != NULL && 26591f0ebf7SArnaldo Carvalho de Melo ccid->ccid_ops->ccid_hc_tx_init(ccid, sk) != 0) 26691f0ebf7SArnaldo Carvalho de Melo goto out_free_ccid; 26791f0ebf7SArnaldo Carvalho de Melo } 26891f0ebf7SArnaldo Carvalho de Melo out: 2697c657876SArnaldo Carvalho de Melo return ccid; 27091f0ebf7SArnaldo Carvalho de Melo out_free_ccid: 27191f0ebf7SArnaldo Carvalho de Melo kmem_cache_free(rx ? ccid_ops->ccid_hc_rx_slab : 27291f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_hc_tx_slab, ccid); 2737c657876SArnaldo Carvalho de Melo ccid = NULL; 2747c657876SArnaldo Carvalho de Melo goto out; 2757c657876SArnaldo Carvalho de Melo } 2767c657876SArnaldo Carvalho de Melo 27791f0ebf7SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(ccid_new); 2787c657876SArnaldo Carvalho de Melo 27991f0ebf7SArnaldo Carvalho de Melo static void ccid_delete(struct ccid *ccid, struct sock *sk, int rx) 28091f0ebf7SArnaldo Carvalho de Melo { 28191f0ebf7SArnaldo Carvalho de Melo struct ccid_operations *ccid_ops; 28291f0ebf7SArnaldo Carvalho de Melo 2837c657876SArnaldo Carvalho de Melo if (ccid == NULL) 2847c657876SArnaldo Carvalho de Melo return; 2857c657876SArnaldo Carvalho de Melo 28691f0ebf7SArnaldo Carvalho de Melo ccid_ops = ccid->ccid_ops; 28791f0ebf7SArnaldo Carvalho de Melo if (rx) { 28891f0ebf7SArnaldo Carvalho de Melo if (ccid_ops->ccid_hc_rx_exit != NULL) 28991f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_hc_rx_exit(sk); 29091f0ebf7SArnaldo Carvalho de Melo kmem_cache_free(ccid_ops->ccid_hc_rx_slab, ccid); 29191f0ebf7SArnaldo Carvalho de Melo } else { 29291f0ebf7SArnaldo Carvalho de Melo if (ccid_ops->ccid_hc_tx_exit != NULL) 29391f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_hc_tx_exit(sk); 29491f0ebf7SArnaldo Carvalho de Melo kmem_cache_free(ccid_ops->ccid_hc_tx_slab, ccid); 2957c657876SArnaldo Carvalho de Melo } 2967c657876SArnaldo Carvalho de Melo } 2977c657876SArnaldo Carvalho de Melo 29891f0ebf7SArnaldo Carvalho de Melo void ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk) 29991f0ebf7SArnaldo Carvalho de Melo { 30091f0ebf7SArnaldo Carvalho de Melo ccid_delete(ccid, sk, 1); 30191f0ebf7SArnaldo Carvalho de Melo } 30291f0ebf7SArnaldo Carvalho de Melo 30391f0ebf7SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(ccid_hc_rx_delete); 30491f0ebf7SArnaldo Carvalho de Melo 30591f0ebf7SArnaldo Carvalho de Melo void ccid_hc_tx_delete(struct ccid *ccid, struct sock *sk) 30691f0ebf7SArnaldo Carvalho de Melo { 30791f0ebf7SArnaldo Carvalho de Melo ccid_delete(ccid, sk, 0); 30891f0ebf7SArnaldo Carvalho de Melo } 30991f0ebf7SArnaldo Carvalho de Melo 31091f0ebf7SArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(ccid_hc_tx_delete); 311*ddebc973SGerrit Renker 312*ddebc973SGerrit Renker int __init ccid_initialize_builtins(void) 313*ddebc973SGerrit Renker { 314*ddebc973SGerrit Renker int i, err; 315*ddebc973SGerrit Renker 316*ddebc973SGerrit Renker for (i = 0; i < ARRAY_SIZE(ccids); i++) { 317*ddebc973SGerrit Renker err = ccid_activate(ccids[i]); 318*ddebc973SGerrit Renker if (err) 319*ddebc973SGerrit Renker goto unwind_registrations; 320*ddebc973SGerrit Renker } 321*ddebc973SGerrit Renker return 0; 322*ddebc973SGerrit Renker 323*ddebc973SGerrit Renker unwind_registrations: 324*ddebc973SGerrit Renker while(--i >= 0) 325*ddebc973SGerrit Renker ccid_deactivate(ccids[i]); 326*ddebc973SGerrit Renker return err; 327*ddebc973SGerrit Renker } 328*ddebc973SGerrit Renker 329*ddebc973SGerrit Renker void ccid_cleanup_builtins(void) 330*ddebc973SGerrit Renker { 331*ddebc973SGerrit Renker int i; 332*ddebc973SGerrit Renker 333*ddebc973SGerrit Renker for (i = 0; i < ARRAY_SIZE(ccids); i++) 334*ddebc973SGerrit Renker ccid_deactivate(ccids[i]); 335*ddebc973SGerrit Renker } 336