aboutsummaryrefslogblamecommitdiffstats
path: root/scripts/gen_initramfs_list.sh
blob: 3eea8f15131bcde957bc0fc9699bd5f47934f62a (plain) (tree)
1
2
3
4
5
6
7
8

                                                          
                                                    
 
                                         
 
                                                                     
                                           









                                                                              

                                                                              
                                                          

                                                                      
                                                            

                                                                      
                                                               
                                                                       









                                                                    





                                            



                              

                     
                               




                                                          

                                                      
























                                                     



                    
               

                          

                                                                           
          






                                                        

 


                                                                          









                                             

                                                                                  









                                                                 







                                                                          

                          
                                                             






                                                               
                                  



                










                                                                          
         










                                                             
                                                           









                                                                   

 






                                                                             
                                                                          

                                                             
                                                                                              





                                                         
                    




                                                                     
                  

                                 
            
                                                      



                      
       

          




                    
                   
                  
 



                                                              
                                           

                     
                                                         



                                                                     



                                                                             


                     



                      
                                                


                                     
                                                


                                     
                                                        
                                           
                                                    







                                      
                                                      
                                          

                                                                     





                                          
                                                                     








                                                                      


                                                           

                                                                    
          

                                               
      
#!/bin/bash
# Copyright (C) Martin Schlemmer <azarah@nosferatu.za.org>
# Copyright (C) 2006 Sam Ravnborg <sam@ravnborg.org>
#
# Released under the terms of the GNU GPL
#
# Generate a cpio packed initramfs. It uses gen_init_cpio to generate
# the cpio archive, and then compresses it.
# The script may also be used to generate the inputfile used for gen_init_cpio
# This script assumes that gen_init_cpio is located in usr/ directory

# error out on errors
set -e

usage() {
cat << EOF
Usage:
$0 [-o <file>] [-u <uid>] [-g <gid>] {-d | <cpio_source>} ...
	-o <file>      Create compressed initramfs file named <file> using
		       gen_init_cpio and compressor depending on the extension
	-u <uid>       User ID to map to user ID 0 (root).
		       <uid> is only meaningful if <cpio_source> is a
		       directory.  "squash" forces all files to uid 0.
	-g <gid>       Group ID to map to group ID 0 (root).
		       <gid> is only meaningful if <cpio_source> is a
		       directory.  "squash" forces all files to gid 0.
	<cpio_source>  File list or directory for cpio archive.
		       If <cpio_source> is a .cpio file it will be used
		       as direct input to initramfs.
	-d             Output the default cpio list.

All options except -o and -l may be repeated and are interpreted
sequentially and immediately.  -u and -g states are preserved across
<cpio_source> options so an explicit "-u 0 -g 0" is required
to reset the root/group mapping.
EOF
}

# awk style field access
# $1 - field number; rest is argument string
field() {
	shift $1 ; echo $1
}

list_default_initramfs() {
	# echo usr/kinit/kinit
	:
}

default_initramfs() {
	cat <<-EOF >> ${output}
		# This is a very simple, default initramfs

		dir /dev 0755 0 0
		nod /dev/console 0600 0 0 c 5 1
		dir /root 0700 0 0
		# file /kinit usr/kinit/kinit 0755 0 0
		# slink /init kinit 0755 0 0
	EOF
}

filetype() {
	local argv1="$1"

	# symlink test must come before file test
	if [ -L "${argv1}" ]; then
		echo "slink"
	elif [ -f "${argv1}" ]; then
		echo "file"
	elif [ -d "${argv1}" ]; then
		echo "dir"
	elif [ -b "${argv1}" -o -c "${argv1}" ]; then
		echo "nod"
	elif [ -p "${argv1}" ]; then
		echo "pipe"
	elif [ -S "${argv1}" ]; then
		echo "sock"
	else
		echo "invalid"
	fi
	return 0
}

list_print_mtime() {
	:
}

print_mtime() {
	local my_mtime="0"

	if [ -e "$1" ]; then
		my_mtime=$(find "$1" -printf "%T@\n" | sort -r | head -n 1)
	fi

	echo "# Last modified: ${my_mtime}" >> ${output}
	echo "" >> ${output}
}

list_parse() {
	echo "$1 \\"
}

# for each file print a line in following format
# <filetype> <name> <path to file> <octal mode> <uid> <gid>
# for links, devices etc the format differs. See gen_init_cpio for details
parse() {
	local location="$1"
	local name="${location/${srcdir}//}"
	# change '//' into '/'
	name="${name//\/\///}"
	local mode="$2"
	local uid="$3"
	local gid="$4"
	local ftype=$(filetype "${location}")
	# remap uid/gid to 0 if necessary
	[ "$root_uid" = "squash" ] && uid=0 || [ "$uid" -eq "$root_uid" ] && uid=0
	[ "$root_gid" = "squash" ] && gid=0 || [ "$gid" -eq "$root_gid" ] && gid=0
	local str="${mode} ${uid} ${gid}"

	[ "${ftype}" == "invalid" ] && return 0
	[ "${location}" == "${srcdir}" ] && return 0

	case "${ftype}" in
		"file")
			str="${ftype} ${name} ${location} ${str}"
			;;
		"nod")
			local dev=`LC_ALL=C ls -l "${location}"`
			local maj=`field 5 ${dev}`
			local min=`field 6 ${dev}`
			maj=${maj%,}

			[ -b "${location}" ] && dev="b" || dev="c"

			str="${ftype} ${name} ${str} ${dev} ${maj} ${min}"
			;;
		"slink")
			local target=`readlink "${location}"`
			str="${ftype} ${name} ${target} ${str}"
			;;
		*)
			str="${ftype} ${name} ${str}"
			;;
	esac

	echo "${str}" >> ${output}

	return 0
}

unknown_option() {
	printf "ERROR: unknown option \"$arg\"\n" >&2
	printf "If the filename validly begins with '-', " >&2
	printf "then it must be prefixed\n" >&2
	printf "by './' so that it won't be interpreted as an option." >&2
	printf "\n" >&2
	usage >&2
	exit 1
}

list_header() {
	:
}

header() {
	printf "\n#####################\n# $1\n" >> ${output}
}

# process one directory (incl sub-directories)
dir_filelist() {
	${dep_list}header "$1"

	srcdir=$(echo "$1" | sed -e 's://*:/:g')
	dirlist=$(find "${srcdir}" -printf "%p %m %U %G\n")

	# If $dirlist is only one line, then the directory is empty
	if [  "$(echo "${dirlist}" | wc -l)" -gt 1 ]; then
		${dep_list}print_mtime "$1"

		echo "${dirlist}" | \
		while read x; do
			${dep_list}parse ${x}
		done
	fi
}

# if only one file is specified and it is .cpio file then use it direct as fs
# if a directory is specified then add all files in given direcotry to fs
# if a regular file is specified assume it is in gen_initramfs format
input_file() {
	source="$1"
	if [ -f "$1" ]; then
		${dep_list}header "$1"
		is_cpio="$(echo "$1" | sed 's/^.*\.cpio\(\..*\)\?/cpio/')"
		if [ $2 -eq 0 -a ${is_cpio} == "cpio" ]; then
			cpio_file=$1
			echo "$1" | grep -q '^.*\.cpio\..*' && is_cpio_compressed="compressed"
			[ ! -z ${dep_list} ] && echo "$1"
			return 0
		fi
		if [ -z ${dep_list} ]; then
			print_mtime "$1" >> ${output}
			cat "$1"         >> ${output}
		else
			cat "$1" | while read type dir file perm ; do
				if [ "$type" == "file" ]; then
					echo "$file \\";
				fi
			done
		fi
	elif [ -d "$1" ]; then
		dir_filelist "$1"
	else
		echo "  ${prog}: Cannot open '$1'" >&2
		exit 1
	fi
}

prog=$0
root_uid=0
root_gid=0
dep_list=
cpio_file=
cpio_list=
output="/dev/stdout"
output_file=""
is_cpio_compressed=
compr="gzip -9 -f"

arg="$1"
case "$arg" in
	"-l")	# files included in initramfs - used by kbuild
		dep_list="list_"
		echo "deps_initramfs := \\"
		shift
		;;
	"-o")	# generate compressed cpio image named $1
		shift
		output_file="$1"
		cpio_list="$(mktemp ${TMPDIR:-/tmp}/cpiolist.XXXXXX)"
		output=${cpio_list}
		echo "$output_file" | grep -q "\.gz$" && compr="gzip -9 -f"
		echo "$output_file" | grep -q "\.bz2$" && compr="bzip2 -9 -f"
		echo "$output_file" | grep -q "\.lzma$" && compr="lzma -9 -f"
		echo "$output_file" | grep -q "\.cpio$" && compr="cat"
		shift
		;;
esac
while [ $# -gt 0 ]; do
	arg="$1"
	shift
	case "$arg" in
		"-u")	# map $1 to uid=0 (root)
			root_uid="$1"
			shift
			;;
		"-g")	# map $1 to gid=0 (root)
			root_gid="$1"
			shift
			;;
		"-d")	# display default initramfs list
			default_list="$arg"
			${dep_list}default_initramfs
			;;
		"-h")
			usage
			exit 0
			;;
		*)
			case "$arg" in
				"-"*)
					unknown_option
					;;
				*)	# input file/dir - process it
					input_file "$arg" "$#"
					;;
			esac
			;;
	esac
done

# If output_file is set we will generate cpio archive and compress it
# we are carefull to delete tmp files
if [ ! -z ${output_file} ]; then
	if [ -z ${cpio_file} ]; then
		cpio_tfile="$(mktemp ${TMPDIR:-/tmp}/cpiofile.XXXXXX)"
		usr/gen_init_cpio ${cpio_list} > ${cpio_tfile}
	else
		cpio_tfile=${cpio_file}
	fi
	rm ${cpio_list}
	if [ "${is_cpio_compressed}" = "compressed" ]; then
		cat ${cpio_tfile} > ${output_file}
	else
		(cat ${cpio_tfile} | ${compr}  - > ${output_file}) \
		|| (rm -f ${output_file} ; false)
	fi
	[ -z ${cpio_file} ] && rm ${cpio_tfile}
fi
exit 0
="hl kwb">struct net_device *dev) { struct wilc_vif *vif = netdev_priv(dev); struct wilc *wilc = vif->wilc; wilc->txq_thread = kthread_run(linux_wlan_txq_task, (void *)dev, "K_TXQ_TASK"); if (IS_ERR(wilc->txq_thread)) { netdev_err(dev, "couldn't create TXQ thread\n"); wilc->close = 0; return PTR_ERR(wilc->txq_thread); } wait_for_completion(&wilc->txq_thread_started); return 0; } static int wilc_wlan_initialize(struct net_device *dev, struct wilc_vif *vif) { int ret = 0; struct wilc *wl = vif->wilc; if (!wl->initialized) { wl->mac_status = WILC_MAC_STATUS_INIT; wl->close = 0; wlan_init_locks(dev); ret = wilc_wlan_init(dev); if (ret < 0) { ret = -EIO; goto fail_locks; } if (wl->gpio_irq && init_irq(dev)) { ret = -EIO; goto fail_locks; } ret = wlan_initialize_threads(dev); if (ret < 0) { ret = -EIO; goto fail_wilc_wlan; } if (!wl->dev_irq_num && wl->hif_func->enable_interrupt && wl->hif_func->enable_interrupt(wl)) { ret = -EIO; goto fail_irq_init; } if (wilc_wlan_get_firmware(dev)) { ret = -EIO; goto fail_irq_enable; } ret = wilc1000_firmware_download(dev); if (ret < 0) { ret = -EIO; goto fail_irq_enable; } ret = linux_wlan_start_firmware(dev); if (ret < 0) { ret = -EIO; goto fail_irq_enable; } if (wilc_wlan_cfg_get(vif, 1, WID_FIRMWARE_VERSION, 1, 0)) { int size; char firmware_ver[20]; size = wilc_wlan_cfg_get_val(wl, WID_FIRMWARE_VERSION, firmware_ver, sizeof(firmware_ver)); firmware_ver[size] = '\0'; netdev_dbg(dev, "Firmware Ver = %s\n", firmware_ver); } ret = linux_wlan_init_fw_config(dev, vif); if (ret < 0) { netdev_err(dev, "Failed to configure firmware\n"); ret = -EIO; goto fail_fw_start; } wl->initialized = true; return 0; fail_fw_start: wilc_wlan_stop(wl); fail_irq_enable: if (!wl->dev_irq_num && wl->hif_func->disable_interrupt) wl->hif_func->disable_interrupt(wl); fail_irq_init: if (wl->dev_irq_num) deinit_irq(dev); wlan_deinitialize_threads(dev); fail_wilc_wlan: wilc_wlan_cleanup(dev); fail_locks: wlan_deinit_locks(dev); netdev_err(dev, "WLAN initialization FAILED\n"); } else { netdev_dbg(dev, "wilc1000 already initialized\n"); } return ret; } static int mac_init_fn(struct net_device *ndev) { netif_start_queue(ndev); netif_stop_queue(ndev); return 0; } static int wilc_mac_open(struct net_device *ndev) { struct wilc_vif *vif = netdev_priv(ndev); struct wilc *wl = vif->wilc; struct wilc_priv *priv = wdev_priv(vif->ndev->ieee80211_ptr); unsigned char mac_add[ETH_ALEN] = {0}; int ret = 0; int i = 0; if (!wl || !wl->dev) { netdev_err(ndev, "device not ready\n"); return -ENODEV; } netdev_dbg(ndev, "MAC OPEN[%p]\n", ndev); ret = wilc_init_host_int(ndev); if (ret < 0) return ret; ret = wilc_wlan_initialize(ndev, vif); if (ret < 0) { wilc_deinit_host_int(ndev); return ret; } for (i = 0; i < wl->vif_num; i++) { if (ndev == wl->vif[i]->ndev) { wilc_set_wfi_drv_handler(vif, wilc_get_vif_idx(vif), vif->iftype, vif->ifc_id); wilc_set_operation_mode(vif, vif->iftype); break; } } wilc_get_mac_address(vif, mac_add); netdev_dbg(ndev, "Mac address: %pM\n", mac_add); memcpy(wl->vif[i]->src_addr, mac_add, ETH_ALEN); memcpy(ndev->dev_addr, wl->vif[i]->src_addr, ETH_ALEN); if (!is_valid_ether_addr(ndev->dev_addr)) { netdev_err(ndev, "Wrong MAC address\n"); wilc_deinit_host_int(ndev); wilc_wlan_deinitialize(ndev); return -EINVAL; } wilc_mgmt_frame_register(vif->ndev->ieee80211_ptr->wiphy, vif->ndev->ieee80211_ptr, vif->frame_reg[0].type, vif->frame_reg[0].reg); wilc_mgmt_frame_register(vif->ndev->ieee80211_ptr->wiphy, vif->ndev->ieee80211_ptr, vif->frame_reg[1].type, vif->frame_reg[1].reg); netif_wake_queue(ndev); wl->open_ifcs++; priv->p2p.local_random = 0x01; vif->mac_opened = 1; return 0; } static struct net_device_stats *mac_stats(struct net_device *dev) { struct wilc_vif *vif = netdev_priv(dev); return &vif->netstats; } static void wilc_set_multicast_list(struct net_device *dev) { struct netdev_hw_addr *ha; struct wilc_vif *vif = netdev_priv(dev); int i; u8 *mc_list; u8 *cur_mc; if (dev->flags & IFF_PROMISC) return; if (dev->flags & IFF_ALLMULTI || dev->mc.count > WILC_MULTICAST_TABLE_SIZE) { wilc_setup_multicast_filter(vif, 0, 0, NULL); return; } if (dev->mc.count == 0) { wilc_setup_multicast_filter(vif, 1, 0, NULL); return; } mc_list = kmalloc_array(dev->mc.count, ETH_ALEN, GFP_KERNEL); if (!mc_list) return; cur_mc = mc_list; i = 0; netdev_for_each_mc_addr(ha, dev) { memcpy(cur_mc, ha->addr, ETH_ALEN); netdev_dbg(dev, "Entry[%d]: %pM\n", i, cur_mc); i++; cur_mc += ETH_ALEN; } if (wilc_setup_multicast_filter(vif, 1, dev->mc.count, mc_list)) kfree(mc_list); } static void linux_wlan_tx_complete(void *priv, int status) { struct tx_complete_data *pv_data = priv; dev_kfree_skb(pv_data->skb); kfree(pv_data); } netdev_tx_t wilc_mac_xmit(struct sk_buff *skb, struct net_device *ndev) { struct wilc_vif *vif = netdev_priv(ndev); struct wilc *wilc = vif->wilc; struct tx_complete_data *tx_data = NULL; int queue_count; if (skb->dev != ndev) { netdev_err(ndev, "Packet not destined to this device\n"); return 0; } tx_data = kmalloc(sizeof(*tx_data), GFP_ATOMIC); if (!tx_data) { dev_kfree_skb(skb); netif_wake_queue(ndev); return 0; } tx_data->buff = skb->data; tx_data->size = skb->len; tx_data->skb = skb; vif->netstats.tx_packets++; vif->netstats.tx_bytes += tx_data->size; tx_data->bssid = wilc->vif[vif->idx]->bssid; queue_count = wilc_wlan_txq_add_net_pkt(ndev, (void *)tx_data, tx_data->buff, tx_data->size, linux_wlan_tx_complete); if (queue_count > FLOW_CONTROL_UPPER_THRESHOLD) { if (wilc->vif[0]->mac_opened) netif_stop_queue(wilc->vif[0]->ndev); if (wilc->vif[1]->mac_opened) netif_stop_queue(wilc->vif[1]->ndev); } return 0; } static int wilc_mac_close(struct net_device *ndev) { struct wilc_vif *vif = netdev_priv(ndev); struct wilc *wl = vif->wilc; netdev_dbg(ndev, "Mac close\n"); if (wl->open_ifcs > 0) wl->open_ifcs--; else return 0; if (vif->ndev) { netif_stop_queue(vif->ndev); wilc_deinit_host_int(vif->ndev); } if (wl->open_ifcs == 0) { netdev_dbg(ndev, "Deinitializing wilc1000\n"); wl->close = 1; wilc_wlan_deinitialize(ndev); } vif->mac_opened = 0; return 0; } void wilc_frmw_to_linux(struct wilc *wilc, u8 *buff, u32 size, u32 pkt_offset) { unsigned int frame_len = 0; int stats; unsigned char *buff_to_send = NULL; struct sk_buff *skb; struct net_device *wilc_netdev; struct wilc_vif *vif; if (!wilc) return; wilc_netdev = get_if_handler(wilc, buff); if (!wilc_netdev) return; buff += pkt_offset; vif = netdev_priv(wilc_netdev); if (size > 0) { frame_len = size; buff_to_send = buff; skb = dev_alloc_skb(frame_len); if (!skb) return; skb->dev = wilc_netdev; skb_put_data(skb, buff_to_send, frame_len); skb->protocol = eth_type_trans(skb, wilc_netdev); vif->netstats.rx_packets++; vif->netstats.rx_bytes += frame_len; skb->ip_summed = CHECKSUM_UNNECESSARY; stats = netif_rx(skb); netdev_dbg(wilc_netdev, "netif_rx ret value is: %d\n", stats); } } void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size) { int i = 0; struct wilc_vif *vif; for (i = 0; i < wilc->vif_num; i++) { vif = netdev_priv(wilc->vif[i]->ndev); if (vif->monitor_flag) { wilc_wfi_monitor_rx(wilc->monitor_dev, buff, size); return; } } vif = netdev_priv(wilc->vif[1]->ndev); if ((buff[0] == vif->frame_reg[0].type && vif->frame_reg[0].reg) || (buff[0] == vif->frame_reg[1].type && vif->frame_reg[1].reg)) wilc_wfi_p2p_rx(wilc->vif[1]->ndev, buff, size); } static const struct net_device_ops wilc_netdev_ops = { .ndo_init = mac_init_fn, .ndo_open = wilc_mac_open, .ndo_stop = wilc_mac_close, .ndo_start_xmit = wilc_mac_xmit, .ndo_get_stats = mac_stats, .ndo_set_rx_mode = wilc_set_multicast_list, }; static int dev_state_ev_handler(struct notifier_block *this, unsigned long event, void *ptr) { struct in_ifaddr *dev_iface = ptr; struct wilc_priv *priv; struct host_if_drv *hif_drv; struct net_device *dev; struct wilc_vif *vif; if (!dev_iface || !dev_iface->ifa_dev || !dev_iface->ifa_dev->dev) return NOTIFY_DONE; dev = (struct net_device *)dev_iface->ifa_dev->dev; if (dev->netdev_ops != &wilc_netdev_ops) return NOTIFY_DONE; if (!dev->ieee80211_ptr || !dev->ieee80211_ptr->wiphy) return NOTIFY_DONE; priv = wiphy_priv(dev->ieee80211_ptr->wiphy); if (!priv) return NOTIFY_DONE; hif_drv = (struct host_if_drv *)priv->hif_drv; vif = netdev_priv(dev); if (!vif || !hif_drv) return NOTIFY_DONE; switch (event) { case NETDEV_UP: if (vif->iftype == WILC_STATION_MODE || vif->iftype == WILC_CLIENT_MODE) { hif_drv->ifc_up = 1; vif->obtaining_ip = false; del_timer(&vif->during_ip_timer); } if (vif->wilc->enable_ps) wilc_set_power_mgmt(vif, 1, 0); break; case NETDEV_DOWN: if (vif->iftype == WILC_STATION_MODE || vif->iftype == WILC_CLIENT_MODE) { hif_drv->ifc_up = 0; vif->obtaining_ip = false; wilc_set_power_mgmt(vif, 0, 0); } wilc_resolve_disconnect_aberration(vif); break; default: break; } return NOTIFY_DONE; } static struct notifier_block g_dev_notifier = { .notifier_call = dev_state_ev_handler }; void wilc_netdev_cleanup(struct wilc *wilc) { int i; if (!wilc) return; if (wilc->vif[0]->ndev || wilc->vif[1]->ndev) unregister_inetaddr_notifier(&g_dev_notifier); if (wilc->firmware) { release_firmware(wilc->firmware); wilc->firmware = NULL; } for (i = 0; i < WILC_NUM_CONCURRENT_IFC; i++) { if (wilc->vif[i] && wilc->vif[i]->ndev) { unregister_netdev(wilc->vif[i]->ndev); wilc_free_wiphy(wilc->vif[i]->ndev); free_netdev(wilc->vif[i]->ndev); } } wilc_wfi_deinit_mon_interface(wilc); flush_workqueue(wilc->hif_workqueue); destroy_workqueue(wilc->hif_workqueue); wilc_wlan_cfg_deinit(wilc); kfree(wilc->bus_data); kfree(wilc); } EXPORT_SYMBOL_GPL(wilc_netdev_cleanup); int wilc_netdev_init(struct wilc **wilc, struct device *dev, int io_type, const struct wilc_hif_func *ops) { int i, ret; struct wilc_vif *vif; struct net_device *ndev; struct wilc *wl; wl = kzalloc(sizeof(*wl), GFP_KERNEL); if (!wl) return -ENOMEM; ret = wilc_wlan_cfg_init(wl); if (ret) goto free_wl; *wilc = wl; wl->io_type = io_type; wl->hif_func = ops; wl->enable_ps = true; wl->chip_ps_state = WILC_CHIP_WAKEDUP; INIT_LIST_HEAD(&wl->txq_head.list); INIT_LIST_HEAD(&wl->rxq_head.list); wl->hif_workqueue = create_singlethread_workqueue("WILC_wq"); if (!wl->hif_workqueue) { ret = -ENOMEM; goto free_cfg; } register_inetaddr_notifier(&g_dev_notifier); for (i = 0; i < WILC_NUM_CONCURRENT_IFC; i++) { struct wireless_dev *wdev; ndev = alloc_etherdev(sizeof(struct wilc_vif)); if (!ndev) { ret = -ENOMEM; goto free_ndev; } vif = netdev_priv(ndev); memset(vif, 0, sizeof(struct wilc_vif)); if (i == 0) { strcpy(ndev->name, "wlan%d"); vif->ifc_id = 1; } else { strcpy(ndev->name, "p2p%d"); vif->ifc_id = 0; } vif->wilc = *wilc; vif->ndev = ndev; wl->vif[i] = vif; wl->vif_num = i + 1; vif->idx = i; ndev->netdev_ops = &wilc_netdev_ops; wdev = wilc_create_wiphy(ndev, dev); if (!wdev) { netdev_err(ndev, "Can't register WILC Wiphy\n"); ret = -ENOMEM; goto free_ndev; } SET_NETDEV_DEV(ndev, dev); vif->ndev->ieee80211_ptr = wdev; vif->ndev->ml_priv = vif; wdev->netdev = vif->ndev; vif->netstats.rx_packets = 0; vif->netstats.tx_packets = 0; vif->netstats.rx_bytes = 0; vif->netstats.tx_bytes = 0; ret = register_netdev(ndev); if (ret) goto free_ndev; vif->iftype = WILC_STATION_MODE; vif->mac_opened = 0; } return 0; free_ndev: for (; i >= 0; i--) { if (wl->vif[i]) { if (wl->vif[i]->iftype == WILC_STATION_MODE) unregister_netdev(wl->vif[i]->ndev); if (wl->vif[i]->ndev) { wilc_free_wiphy(wl->vif[i]->ndev); free_netdev(wl->vif[i]->ndev); } } } unregister_inetaddr_notifier(&g_dev_notifier); destroy_workqueue(wl->hif_workqueue); free_cfg: wilc_wlan_cfg_deinit(wl); free_wl: kfree(wl); return ret; } EXPORT_SYMBOL_GPL(wilc_netdev_init); MODULE_LICENSE("GPL");