#accordion-section-menu_locations {
	position: relative;
	margin-bottom: 15px;
}

.menu-in-location,
.menu-in-locations {
	display: block;
	font-weight: 600;
	font-size: 10px;
}

#customize-controls .theme-location-set,
#customize-controls .control-section .accordion-section-title:focus .menu-in-location,
#customize-controls .control-section .accordion-section-title:hover .menu-in-location {
	color: #555;
}

.wp-customizer .menu-item-bar .menu-item-handle,
.wp-customizer .menu-item-settings,
.wp-customizer .menu-item-settings .description-thin {
	-webkit-box-sizing: border-box;
	-moz-box-sizing: border-box;
	box-sizing: border-box;
}

.wp-customizer .menu-item-bar {
	margin: 0;
}

.wp-customizer .menu-item-bar .menu-item-handle {
	width: 100%;
	background: #fff;
}

.wp-customizer .menu-item-handle .item-title {
	margin-left: 0;
}

.wp-customizer .menu-item-handle .item-type {
	padding: 1px 5px 0 21px;
	float: left;
	text-align: left;
}

.wp-customizer .menu-item-settings {
	max-width: 100%;
	overflow: hidden;
	padding: 10px;
	background: #eee;
	border: 1px solid #999;
	border-top: none;
}

.wp-customizer .menu-item-settings .description-thin {
	width: 100%;
	height: auto;
	margin: 0 0 8px 0;
}

.wp-customizer .menu-item-settings input[type="text"] {
	width: 100%;
}

.wp-customizer .menu-item-settings .submitbox {
	margin: 0;
	padding: 0;
}

.wp-customizer .menu-item-settings .link-to-original {
	padding: 5px 0;
	border: none;
	font-style: normal;
	margin: 0;
	width: 100%;
}

.wp-customizer .menu-item .submitbox .submitdelete {
	float: right;
	margin: 6px 0 0;
	padding: 0;
	cursor: pointer;
}

/**
 * Menu items reordering styles
 */

.menu-item-reorder-nav {
	display: none;
	background-color: #fff;
	position: absolute;
	top: 0;
	left: 0;
}

.menus-move-left:before {
	content: "\f345";
}

.menus-move-right:before {
	content: "\f341";
}

.reordering .menu-item .item-controls,
.reordering .menu-item .item-type {
	display: none;
}

.reordering .menu-item-reorder-nav {
	display: block;
}

.customize-control input.menu-name-field {
	width: 100%; /* Override the 98% default for customizer inputs, to align with the size of menu items. */
	margin: 12px 0;
}

.wp-customizer .menu-item .item-edit {
	position: absolute;
	left: -19px;
	top: 2px;
	display: block;
	width: 30px;
	height: 38px;
	margin-left: 0 !important;
	-webkit-box-shadow: none;
	box-shadow: none;
	outline: none;
	overflow: hidden;
	cursor: pointer;
}

.wp-customizer .menu-item.menu-item-edit-active .item-edit .toggle-indicator:after {
	content: "\f142";
}

.wp-customizer .menu-item-settings p.description {
	font-style: normal;
}

.wp-customizer .menu-settings dl {
	margin: 12px 0 0 0;
	padding: 0;
}

.wp-customizer .menu-settings .checkbox-input {
	margin-top: 8px;
}

.wp-customizer .menu-settings .menu-theme-locations {
	border-top: 1px solid #ccc;
}

.wp-customizer .menu-settings {
	margin-top: 36px;
	border-top: none;
}

.menu-settings .customize-control-checkbox label {
	line-height: 1;
}

/* @todo update selector or potentially remove */
.menu-settings .customize-control.customize-control-checkbox {
	margin-bottom: 8px; /* Override collapsing at smaller viewports. */
}

.customize-control-menu {
	margin-top: 4px;
}

#customize-controls .customize-info.open.active-menu-screen-options .customize-help-toggle {
	color: #555;
}

/* Screen Options */
.customize-screen-options-toggle {
	background: none;
	border: none;
	color: #555;
	cursor: pointer;
	margin: 0;
	padding: 20px;
	position: absolute;
	left: 0;
	top: 30px;
}

#customize-controls .customize-info .customize-help-toggle {
	padding: 20px;
}

#customize-controls .customize-info .customize-help-toggle:before {
	padding: 4px;
}

.customize-screen-options-toggle:hover,
.customize-screen-options-toggle:active,
.customize-screen-options-toggle:focus,
.active-menu-screen-options .customize-screen-options-toggle,
#customize-controls .customize-info.open.active-menu-screen-options .customize-help-toggle:hover,
#customize-controls .customize-info.open.active-menu-screen-options .customize-help-toggle:active,
#customize-controls .customize-info.open.active-menu-screen-options .customize-help-toggle:focus {
	color: #0073aa;
}

.customize-screen-options-toggle:focus,
#customize-controls .customize-info .customize-help-toggle:focus {
	outline: none;
}

.customize-screen-options-toggle:before {
	-moz-osx-font-smoothing: grayscale;
	border: none;
	content: "\f111";
	display: block;
	font: 18px/1 dashicons;
	padding: 5px;
	text-align: center;
	text-decoration: none !important;
	text-indent: 0;
	right: 6px;
	position: absolute;
	top: 6px;
}

.customize-screen-options-toggle:focus:before,
#customize-controls .customize-info .customize-help-toggle:focus:before {
	-webkit-border-radius: 100%;
	border-radius: 100%;
}

.wp-customizer #screen-options-wrap {
	display: none;
	background: #fff;
	border-top: 1px solid #ddd;
	padding: 4px 15px 15px;
}

.wp-customizer .metabox-prefs label {
	display: block;
	padding-left: 0;
	line-height: 30px;
}

/* rework the arrow indicator implementation for NVDA bug same as #32715 */
.wp-customizer .toggle-indicator {
	display: inline-block;
	font-size: 20px;
	line-height: 1;
	text-indent: -1px; /* account for the dashicon alignment */
}

.rtl .wp-customizer .toggle-indicator {
	text-indent: 1px; /* account for the dashicon alignment */
}

.wp-customizer .toggle-indicator:after {
	content: "\f140";
	speak: none;
	vertical-align: top;
	-webkit-border-radius: 50%;
	border-radius: 50%;
	color: #72777c;
	font: normal 20px/1 dashicons;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	text-decoration: none !important;
}

#accordion-panel-nav_menus .field-link-target,
#accordion-panel-nav_menus .field-title-attribute,
#accordion-panel-nav_menus .field-css-classes,
#accordion-panel-nav_menus .field-xfn,
#accordion-panel-nav_menus .field-description {
	display: none;
}

#accordion-panel-nav_menus.field-link-target-active .field-link-target,
#accordion-panel-nav_menus.field-title-attribute-active .field-title-attribute,
#accordion-panel-nav_menus.field-css-classes-active .field-css-classes,
#accordion-panel-nav_menus.field-xfn-active .field-xfn,
#accordion-panel-nav_menus.field-description-active .field-description {
	display: block;
}

/* WARNING: The 20px factor is hard-coded in JS. */
.menu-item-depth-0  { margin-right: 0;     }
.menu-item-depth-1  { margin-right: 20px;  }
.menu-item-depth-2  { margin-right: 40px;  }
.menu-item-depth-3  { margin-right: 60px;  }
.menu-item-depth-4  { margin-right: 80px;  }
.menu-item-depth-5  { margin-right: 100px; }
.menu-item-depth-6  { margin-right: 120px; }
.menu-item-depth-7  { margin-right: 140px; }
.menu-item-depth-8  { margin-right: 160px; } /* Not likely to be used or useful beyond this depth */
.menu-item-depth-9  { margin-right: 180px; }
.menu-item-depth-10 { margin-right: 200px; }
.menu-item-depth-11 { margin-right: 220px; }

/* @todo handle .menu-item-settings width */
.menu-item-depth-0  > .menu-item-bar { margin-left: 0;     }
.menu-item-depth-1  > .menu-item-bar { margin-left: 20px;  }
.menu-item-depth-2  > .menu-item-bar { margin-left: 40px;  }
.menu-item-depth-3  > .menu-item-bar { margin-left: 60px;  }
.menu-item-depth-4  > .menu-item-bar { margin-left: 80px;  }
.menu-item-depth-5  > .menu-item-bar { margin-left: 100px; }
.menu-item-depth-6  > .menu-item-bar { margin-left: 120px; }
.menu-item-depth-7  > .menu-item-bar { margin-left: 140px; }
.menu-item-depth-8  > .menu-item-bar { margin-left: 160px; }
.menu-item-depth-9  > .menu-item-bar { margin-left: 180px; }
.menu-item-depth-10 > .menu-item-bar { margin-left: 200px; }
.menu-item-depth-11 > .menu-item-bar { margin-left: 220px; }

/* Submenu left margin. */
.menu-item-depth-0  .menu-item-transport { margin-right: 0;      }
.menu-item-depth-1  .menu-item-transport { margin-right: -20px;  }
.menu-item-depth-3  .menu-item-transport { margin-right: -60px;  }
.menu-item-depth-4  .menu-item-transport { margin-right: -80px;  }
.menu-item-depth-2  .menu-item-transport { margin-right: -40px;  }
.menu-item-depth-5  .menu-item-transport { margin-right: -100px; }
.menu-item-depth-6  .menu-item-transport { margin-right: -120px; }
.menu-item-depth-7  .menu-item-transport { margin-right: -140px; }
.menu-item-depth-8  .menu-item-transport { margin-right: -160px; }
.menu-item-depth-9  .menu-item-transport { margin-right: -180px; }
.menu-item-depth-10 .menu-item-transport { margin-right: -200px; }
.menu-item-depth-11 .menu-item-transport { margin-right: -220px; }

/* WARNING: The 20px factor is hard-coded in JS. */
.reordering .menu-item-depth-0  { margin-right: 0;     }
.reordering .menu-item-depth-1  { margin-right: 15px;  }
.reordering .menu-item-depth-2  { margin-right: 30px;  }
.reordering .menu-item-depth-3  { margin-right: 45px;  }
.reordering .menu-item-depth-4  { margin-right: 60px;  }
.reordering .menu-item-depth-5  { margin-right: 75px;  }
.reordering .menu-item-depth-6  { margin-right: 90px;  }
.reordering .menu-item-depth-7  { margin-right: 105px; }
.reordering .menu-item-depth-8  { margin-right: 120px; } /* Not likely to be used or useful beyond this depth */
.reordering .menu-item-depth-9  { margin-right: 135px; }
.reordering .menu-item-depth-10 { margin-right: 150px; }
.reordering .menu-item-depth-11 { margin-right: 165px; }

.reordering .menu-item-depth-0  > .menu-item-bar { margin-left: 0;     }
.reordering .menu-item-depth-1  > .menu-item-bar { margin-left: 15px;  }
.reordering .menu-item-depth-2  > .menu-item-bar { margin-left: 30px;  }
.reordering .menu-item-depth-3  > .menu-item-bar { margin-left: 45px;  }
.reordering .menu-item-depth-4  > .menu-item-bar { margin-left: 60px;  }
.reordering .menu-item-depth-5  > .menu-item-bar { margin-left: 75px;  }
.reordering .menu-item-depth-6  > .menu-item-bar { margin-left: 90px;  }
.reordering .menu-item-depth-7  > .menu-item-bar { margin-left: 105px; }
.reordering .menu-item-depth-8  > .menu-item-bar { margin-left: 120px; }
.reordering .menu-item-depth-9  > .menu-item-bar { margin-left: 135px; }
.reordering .menu-item-depth-10 > .menu-item-bar { margin-left: 150px; }
.reordering .menu-item-depth-11 > .menu-item-bar { margin-left: 165px; }

.control-section-nav_menu .menu .menu-item-edit-active {
	margin-right: 0;
}

.control-section-nav_menu .menu .menu-item-edit-active .menu-item-bar {
	margin-left: 0;
}

.control-section-nav_menu .menu .sortable-placeholder {
	margin-top: 0;
	margin-bottom: 1px;
	max-width: -webkit-calc(100% - 2px);
	max-width: calc(100% - 2px);
	float: right;
	display: list-item;
	border-color: #a0a5aa;
}

.menu-item-transport li.customize-control {
	float: none;
}

.control-section-nav_menu .menu ul.menu-item-transport .menu-item-bar {
	margin-top: 0;
}

/**
 * Add-menu-items mode
 */

.adding-menu-items .control-section {
	opacity: .4;
}

.adding-menu-items .control-panel.control-section,
.adding-menu-items .control-section.open {
	opacity: 1;
}

.menu-item-bar .item-delete {
	color: #a00;
	position: absolute;
	top: 2px;
	left: -19px;
	width: 30px;
	height: 38px;
	cursor: pointer;
	display: none;
}

.menu-item-bar .item-delete:before {
	content: "\f335";
	position: absolute;
	top: 9px;
	right: 5px;
	-webkit-border-radius: 50%;
	border-radius: 50%;
	font: normal 20px/1 dashicons;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

.ie8 .menu-item-bar .item-delete:before {
	top: -10px;
}

.menu-item-bar .item-delete:hover,
.menu-item-bar .item-delete:focus {
	-webkit-box-shadow: none;
	box-shadow: none;
	outline: none;
	color: #f00;
}

.adding-menu-items .menu-item-bar .item-edit {
	display: none;
}

.adding-menu-items .menu-item-bar .item-delete {
	display: block;
}

/**
 * Styles for menu-item addition panel
 */

#available-menu-items.opening {
	overflow-y: hidden; /* avoid scrollbar jitter with animating heights */
}

#available-menu-items #available-menu-items-search.open {
	height: 100%;
	border-bottom: none;
}

#available-menu-items .accordion-section-title {
	border-right: none;
	border-left: none;
	background: #fff;
	-webkit-transition: background-color 0.15s;
	transition: background-color 0.15s;
}

#available-menu-items .open .accordion-section-title,
#available-menu-items #available-menu-items-search .accordion-section-title {
	background: #eee;
}

/* rework the arrow indicator implementation for NVDA bug see #32715 */
#available-menu-items .accordion-section-title:after {
	content: none !important;
}

#available-menu-items .accordion-section-title:hover .toggle-indicator:after,
#available-menu-items .button-link:hover .toggle-indicator:after,
#available-menu-items .button-link:focus .toggle-indicator:after {
	color: #23282d;
}

#available-menu-items .open .accordion-section-title .toggle-indicator:after {
	content: "\f142";
	color: #23282d;
}

#available-menu-items .accordion-section-content {
	overflow-y: auto;
	max-height: 200px; /* This gets set in JS to fit the screen size, and based on # of sections. */
	background: transparent;
}

#available-menu-items .accordion-section-title button {
	display: block;
	width: 28px;
	height: 35px;
	position: absolute;
	top: 5px;
	left: 5px;
	-webkit-box-shadow: none;
	box-shadow: none;
	outline: none;
	cursor: pointer;
}

#available-menu-items .accordion-section-title .no-items,
#available-menu-items .cannot-expand .accordion-section-title .spinner,
#available-menu-items .cannot-expand .accordion-section-title > button {
	display: none;
}

#available-menu-items-search.cannot-expand .accordion-section-title .spinner {
	display: block;
}

#available-menu-items .cannot-expand .accordion-section-title .no-items {
	float: left;
	color: #555d66;
	font-weight: normal;
	margin-right: 5px;
}

#available-menu-items .accordion-section-content {
	padding: 1px 15px 15px 15px;
	margin: 0;
	max-height: 290px;
}

#available-menu-items .menu-item-tpl {
	margin: 0;
}

#custom-menu-item-name.invalid,
#custom-menu-item-url.invalid,
.menu-name-field.invalid,
.menu-name-field.invalid:focus {
	border: 1px solid #f00;
}

#available-menu-items .menu-item-handle .item-type {
	padding-left: 0;
}

#available-menu-items .menu-item-handle .item-title {
	padding-right: 20px;
}

#available-menu-items .menu-item-handle {
	cursor: pointer;
}

#available-menu-items .menu-item-handle {
	-webkit-box-shadow: none;
	box-shadow: none;
	margin-top: -1px;
}

#available-menu-items .menu-item-handle:hover {
	z-index: 1;
}

#available-menu-items .item-title h4 {
	padding: 0 0 5px;
	font-size: 14px;
}

#available-menu-items .item-add {
	position: absolute;
	top: 1px;
	right: 1px;
	color: #82878c;
	width: 30px;
	height: 38px;
	-webkit-box-shadow: none;
	box-shadow: none;
	outline: none;
	cursor: pointer;
}

#available-menu-items .menu-item-handle .item-add:focus {
	color: #23282d;
}

#available-menu-items .item-add:before {
	content: "\f543";
	position: relative;
	right: 2px;
	top: 3px;
	display: inline-block;
	height: 20px;
	-webkit-border-radius: 50%;
	border-radius: 50%;
	font: normal 20px/1.05 dashicons; /* line height is to account for the dashicon's vertical alignment */
}

#available-menu-items .menu-item-handle.item-added .item-type,
#available-menu-items .menu-item-handle.item-added .item-title,
#available-menu-items .menu-item-handle.item-added:hover .item-add,
#available-menu-items .menu-item-handle.item-added .item-add:focus {
	color: #82878c;
}

#available-menu-items .menu-item-handle.item-added .item-add:before {
	content: "\f147";
}

#available-menu-items .accordion-section-title.loading .spinner,
#available-menu-items-search.loading .accordion-section-title .spinner {
	visibility: visible;
	margin: 0 20px;
}

#available-menu-items-search .clear-results {
	position: absolute;
	top: 20px;
	left: 20px;
	width: 20px;
	height: 20px;
	cursor: pointer;
	color: #a00;
	text-decoration: none;
}

#available-menu-items-search .clear-results,
#available-menu-items-search.loading .clear-results.is-visible {
	display: none;
}

#available-menu-items-search .clear-results.is-visible {
	display: block;
}

#available-menu-items-search .clear-results:before {
	content: "\f335";
	font: normal 20px/1 dashicons;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

#available-menu-items-search .clear-results:hover,
#available-menu-items-search .clear-results:focus {
	color: #f00;
}

#available-menu-items-search .spinner {
	position: absolute;
	top: 20px;
	margin: 0 !important;
	left: 20px;
}

/* search results list */
#available-menu-items #available-menu-items-search .accordion-section-content {
	position: absolute;
	right: 1px;
	top: 60px; /* below title div / search input */
	bottom: 0px; /* 100% height that still triggers lazy load */
	max-height: none;
	width: 100%;
	padding: 1px 15px 15px;
	-webkit-box-sizing: border-box;
	-moz-box-sizing: border-box;
	box-sizing: border-box;
}

#available-menu-items-search .accordion-section-title:after {
	display: none;
}

#available-menu-items-search .accordion-section-content:empty {
	min-height: 0;
	padding: 0;
}

#available-menu-items-search.loading .accordion-section-content div {
	opacity: .5;
}

#available-menu-items-search.loading.loading-more .accordion-section-content div {
	opacity: 1;
}

#customize-preview {
	-webkit-transition: all 0.2s;
	transition: all 0.2s;
}

body.adding-menu-items #available-menu-items {
	right: 0;
	visibility: visible;
}

body.adding-menu-items .wp-full-overlay-main {
	right: 300px;
}

body.adding-menu-items #customize-preview {
	opacity: 0.4;
}

.menu-item-handle .spinner {
	display: none;
	float: right;
	margin: 0 0 0 8px;
}

.nav-menu-inserted-item-loading .spinner {
	display: block;
}

.nav-menu-inserted-item-loading .menu-item-handle .item-type {
	padding: 0 8px 0 0;
}

.nav-menu-inserted-item-loading .menu-item-handle,
.added-menu-item .menu-item-handle.loading {
	padding: 10px 8px 10px 15px;
	cursor: default;
	opacity: .5;
	background: #fff;
	color: #727773;
}

.added-menu-item .menu-item-handle {
	-webkit-transition-property: opacity, background, color;
	transition-property: opacity, background, color;
	-webkit-transition-duration: 1.25s;
	transition-duration: 1.25s;
	-webkit-transition-timing-function: cubic-bezier( .25, -2.5, .75, 8 );
	transition-timing-function: cubic-bezier( .25, -2.5, .75, 8 ); /* Replacement for .hide().fadeIn('slow') in JS to add emphasis when it's loaded. */
}

/* Add/delete Menus */

/* @todo update selector */
#accordion-section-add_menu {
	margin: 15px 12px;
}

.new-menu-section-content {
	display: none;
	padding: 15px 0 0 0;
	clear: both;
}

/* @todo update selector */
#accordion-section-add_menu .accordion-section-title {
	padding-right: 45px;
}

/* @todo update selector */
#accordion-section-add_menu .accordion-section-title:before {
	font: normal 20px/1 dashicons;
	position: absolute;
	top: 12px;
	right: 14px;
	content: "\f132";
}

#create-new-menu-submit {
	float: left;
	margin: 0 0 12px 0;
}

.menu-delete-item {
	float: right;
	padding: 1em 0;
	width: 100%;
}

li.assigned-to-menu-location .menu-delete-item {
	display: none;
}

li.assigned-to-menu-location .add-new-menu-item {
	margin-bottom: 1em;
}

.menu-delete {
	color: #a00;
	cursor: pointer;
	text-decoration: underline;
}

.menu-delete:hover,
.menu-delete:focus {
	color: #f00;
	text-decoration: none;
}

.menu-item-handle {
	margin-top: -1px;
}
.ui-sortable-disabled .menu-item-handle {
	cursor: default;
}

.menu-item-handle:hover {
	position: relative;
	z-index: 10;
	color: #0073aa;
}

.menu-item-handle:hover .item-type,
.menu-item-handle:hover .item-edit,
#available-menu-items .menu-item-handle:hover .item-add {
	color: #0073aa;
}

.menu-item-edit-active .menu-item-handle {
	border-color: #999;
	border-bottom: none;
}

.customize-control-nav_menu_item {
	margin-bottom: 0;
}

.customize-control-nav_menu {
	margin-top: 12px;
}

/**
 * box-shadows
 */

.wp-customizer .menu-item .submitbox .submitdelete:focus,
.customize-screen-options-toggle:focus:before,
#customize-controls .customize-info .customize-help-toggle:focus:before,
.wp-customizer button:focus .toggle-indicator:after,
#available-menu-items-search .clear-results:focus,
.menu-delete:focus,
.menu-item-bar .item-delete:focus:before,
#available-menu-items .item-add:focus:before {
	-webkit-box-shadow:
		0 0 0 1px #5b9dd9,
		0 0 2px 1px rgba(30, 140, 190, .8);
	box-shadow:
		0 0 0 1px #5b9dd9,
		0 0 2px 1px rgba(30, 140, 190, .8);
}

@media screen and ( max-width: 782px ) {
	#available-menu-items #available-menu-items-search .accordion-section-content {
		top: 63px;
	}
}

@media screen and ( max-width: 640px ) {
	#available-menu-items #available-menu-items-search .accordion-section-content {
		top: 133px;
	}
}


const slugUrl = ":slug([a-z-0-9]+)/:id([0-9]+)/";
export const baseUrl = "/";
export const searchUrl = `${baseUrl}search/`;
export const categoryUrl = `${baseUrl}category/${slugUrl}`;
export const collectionUrl = `${baseUrl}collection/${slugUrl}`;
export const productUrl = `${baseUrl}product/${slugUrl}`;
export const cartUrl = `${baseUrl}cart/:token?/`;
export const checkoutLoginUrl = `${baseUrl}login/`;
export const pageUrl = `${baseUrl}page/:slug/`;
export const guestOrderDetailsUrl = `/order-history/:token/`;
export const accountUrl = `${baseUrl}account/`;
export const accountConfirmUrl = `${baseUrl}account-confirm/`;
export const orderHistoryUrl = `${baseUrl}order-history/`;
export const addressBookUrl = `${baseUrl}address-book/`;
export const passwordResetUrl = `${baseUrl}reset-password/`;
export const checkoutUrl = `${baseUrl}checkout/`;
export const orderFinalizedUrl = `${baseUrl}order-finalized/`;


/*
 *  The driver for the ForteMedia FM801 based soundcards
 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
 *
 *  Support FM only card by Andy Shevchenko <andy@smile.org.ua>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 */

#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/moduleparam.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/tlv.h>
#include <sound/ac97_codec.h>
#include <sound/mpu401.h>
#include <sound/opl3.h>
#include <sound/initval.h>

#include <asm/io.h>

#ifdef CONFIG_SND_FM801_TEA575X_BOOL
#include <sound/tea575x-tuner.h>
#define TEA575X_RADIO 1
#endif

MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("ForteMedia FM801");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{ForteMedia,FM801},"
		"{Genius,SoundMaker Live 5.1}}");

static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
/*
 *  Enable TEA575x tuner
 *    1 = MediaForte 256-PCS
 *    2 = MediaForte 256-PCPR
 *    3 = MediaForte 64-PCR
 *   16 = setup tuner only (this is additional bit), i.e. SF64-PCR FM card
 *  High 16-bits are video (radio) device number + 1
 */
static int tea575x_tuner[SNDRV_CARDS];

module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the FM801 soundcard.");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for the FM801 soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable FM801 soundcard.");
module_param_array(tea575x_tuner, int, NULL, 0444);
MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (1 = SF256-PCS, 2=SF256-PCPR, 3=SF64-PCR, +16=tuner-only).");

#define TUNER_ONLY		(1<<4)
#define TUNER_TYPE_MASK		(~TUNER_ONLY & 0xFFFF)

/*
 *  Direct registers
 */

#define FM801_REG(chip, reg)	(chip->port + FM801_##reg)

#define FM801_PCM_VOL		0x00	/* PCM Output Volume */
#define FM801_FM_VOL		0x02	/* FM Output Volume */
#define FM801_I2S_VOL		0x04	/* I2S Volume */
#define FM801_REC_SRC		0x06	/* Record Source */
#define FM801_PLY_CTRL		0x08	/* Playback Control */
#define FM801_PLY_COUNT		0x0a	/* Playback Count */
#define FM801_PLY_BUF1		0x0c	/* Playback Bufer I */
#define FM801_PLY_BUF2		0x10	/* Playback Buffer II */
#define FM801_CAP_CTRL		0x14	/* Capture Control */
#define FM801_CAP_COUNT		0x16	/* Capture Count */
#define FM801_CAP_BUF1		0x18	/* Capture Buffer I */
#define FM801_CAP_BUF2		0x1c	/* Capture Buffer II */
#define FM801_CODEC_CTRL	0x22	/* Codec Control */
#define FM801_I2S_MODE		0x24	/* I2S Mode Control */
#define FM801_VOLUME		0x26	/* Volume Up/Down/Mute Status */
#define FM801_I2C_CTRL		0x29	/* I2C Control */
#define FM801_AC97_CMD		0x2a	/* AC'97 Command */
#define FM801_AC97_DATA		0x2c	/* AC'97 Data */
#define FM801_MPU401_DATA	0x30	/* MPU401 Data */
#define FM801_MPU401_CMD	0x31	/* MPU401 Command */
#define FM801_GPIO_CTRL		0x52	/* General Purpose I/O Control */
#define FM801_GEN_CTRL		0x54	/* General Control */
#define FM801_IRQ_MASK		0x56	/* Interrupt Mask */
#define FM801_IRQ_STATUS	0x5a	/* Interrupt Status */
#define FM801_OPL3_BANK0	0x68	/* OPL3 Status Read / Bank 0 Write */
#define FM801_OPL3_DATA0	0x69	/* OPL3 Data 0 Write */
#define FM801_OPL3_BANK1	0x6a	/* OPL3 Bank 1 Write */
#define FM801_OPL3_DATA1	0x6b	/* OPL3 Bank 1 Write */
#define FM801_POWERDOWN		0x70	/* Blocks Power Down Control */

/* codec access */
#define FM801_AC97_READ		(1<<7)	/* read=1, write=0 */
#define FM801_AC97_VALID	(1<<8)	/* port valid=1 */
#define FM801_AC97_BUSY		(1<<9)	/* busy=1 */
#define FM801_AC97_ADDR_SHIFT	10	/* codec id (2bit) */

/* playback and record control register bits */
#define FM801_BUF1_LAST		(1<<1)
#define FM801_BUF2_LAST		(1<<2)
#define FM801_START		(1<<5)
#define FM801_PAUSE		(1<<6)
#define FM801_IMMED_STOP	(1<<7)
#define FM801_RATE_SHIFT	8
#define FM801_RATE_MASK		(15 << FM801_RATE_SHIFT)
#define FM801_CHANNELS_4	(1<<12)	/* playback only */
#define FM801_CHANNELS_6	(2<<12)	/* playback only */
#define FM801_CHANNELS_6MS	(3<<12)	/* playback only */
#define FM801_CHANNELS_MASK	(3<<12)
#define FM801_16BIT		(1<<14)
#define FM801_STEREO		(1<<15)

/* IRQ status bits */
#define FM801_IRQ_PLAYBACK	(1<<8)
#define FM801_IRQ_CAPTURE	(1<<9)
#define FM801_IRQ_VOLUME	(1<<14)
#define FM801_IRQ_MPU		(1<<15)

/* GPIO control register */
#define FM801_GPIO_GP0		(1<<0)	/* read/write */
#define FM801_GPIO_GP1		(1<<1)
#define FM801_GPIO_GP2		(1<<2)
#define FM801_GPIO_GP3		(1<<3)
#define FM801_GPIO_GP(x)	(1<<(0+(x)))
#define FM801_GPIO_GD0		(1<<8)	/* directions: 1 = input, 0 = output*/
#define FM801_GPIO_GD1		(1<<9)
#define FM801_GPIO_GD2		(1<<10)
#define FM801_GPIO_GD3		(1<<11)
#define FM801_GPIO_GD(x)	(1<<(8+(x)))
#define FM801_GPIO_GS0		(1<<12)	/* function select: */
#define FM801_GPIO_GS1		(1<<13)	/*    1 = GPIO */
#define FM801_GPIO_GS2		(1<<14)	/*    0 = other (S/PDIF, VOL) */
#define FM801_GPIO_GS3		(1<<15)
#define FM801_GPIO_GS(x)	(1<<(12+(x)))

/*

 */

struct fm801 {
	int irq;

	unsigned long port;	/* I/O port number */
	unsigned int multichannel: 1,	/* multichannel support */
		     secondary: 1;	/* secondary codec */
	unsigned char secondary_addr;	/* address of the secondary codec */
	unsigned int tea575x_tuner;	/* tuner access method & flags */

	unsigned short ply_ctrl; /* playback control */
	unsigned short cap_ctrl; /* capture control */

	unsigned long ply_buffer;
	unsigned int ply_buf;
	unsigned int ply_count;
	unsigned int ply_size;
	unsigned int ply_pos;

	unsigned long cap_buffer;
	unsigned int cap_buf;
	unsigned int cap_count;
	unsigned int cap_size;
	unsigned int cap_pos;

	struct snd_ac97_bus *ac97_bus;
	struct snd_ac97 *ac97;
	struct snd_ac97 *ac97_sec;

	struct pci_dev *pci;
	struct snd_card *card;
	struct snd_pcm *pcm;
	struct snd_rawmidi *rmidi;
	struct snd_pcm_substream *playback_substream;
	struct snd_pcm_substream *capture_substream;
	unsigned int p_dma_size;
	unsigned int c_dma_size;

	spinlock_t reg_lock;
	struct snd_info_entry *proc_entry;

#ifdef TEA575X_RADIO
	struct snd_tea575x tea;
#endif

#ifdef CONFIG_PM
	u16 saved_regs[0x20];
#endif
};

static DEFINE_PCI_DEVICE_TABLE(snd_fm801_ids) = {
	{ 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, },   /* FM801 */
	{ 0x5213, 0x0510, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, },   /* Gallant Odyssey Sound 4 */
	{ 0, }
};

MODULE_DEVICE_TABLE(pci, snd_fm801_ids);

/*
 *  common I/O routines
 */

static int snd_fm801_update_bits(struct fm801 *chip, unsigned short reg,
				 unsigned short mask, unsigned short value)
{
	int change;
	unsigned long flags;
	unsigned short old, new;

	spin_lock_irqsave(&chip->reg_lock, flags);
	old = inw(chip->port + reg);
	new = (old & ~mask) | value;
	change = old != new;
	if (change)
		outw(new, chip->port + reg);
	spin_unlock_irqrestore(&chip->reg_lock, flags);
	return change;
}

static void snd_fm801_codec_write(struct snd_ac97 *ac97,
				  unsigned short reg,
				  unsigned short val)
{
	struct fm801 *chip = ac97->private_data;
	int idx;

	/*
	 *  Wait until the codec interface is not ready..
	 */
	for (idx = 0; idx < 100; idx++) {
		if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
			goto ok1;
		udelay(10);
	}
	snd_printk(KERN_ERR "AC'97 interface is busy (1)\n");
	return;

 ok1:
	/* write data and address */
	outw(val, FM801_REG(chip, AC97_DATA));
	outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD));
	/*
	 *  Wait until the write command is not completed..
         */
	for (idx = 0; idx < 1000; idx++) {
		if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
			return;
		udelay(10);
	}
	snd_printk(KERN_ERR "AC'97 interface #%d is busy (2)\n", ac97->num);
}

static unsigned short snd_fm801_codec_read(struct snd_ac97 *ac97, unsigned short reg)
{
	struct fm801 *chip = ac97->private_data;
	int idx;

	/*
	 *  Wait until the codec interface is not ready..
	 */
	for (idx = 0; idx < 100; idx++) {
		if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
			goto ok1;
		udelay(10);
	}
	snd_printk(KERN_ERR "AC'97 interface is busy (1)\n");
	return 0;

 ok1:
	/* read command */
	outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ,
	     FM801_REG(chip, AC97_CMD));
	for (idx = 0; idx < 100; idx++) {
		if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
			goto ok2;
		udelay(10);
	}
	snd_printk(KERN_ERR "AC'97 interface #%d is busy (2)\n", ac97->num);
	return 0;

 ok2:
	for (idx = 0; idx < 1000; idx++) {
		if (inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_VALID)
			goto ok3;
		udelay(10);
	}
	snd_printk(KERN_ERR "AC'97 interface #%d is not valid (2)\n", ac97->num);
	return 0;

 ok3:
	return inw(FM801_REG(chip, AC97_DATA));
}

static unsigned int rates[] = {
  5500,  8000,  9600, 11025,
  16000, 19200, 22050, 32000,
  38400, 44100, 48000
};

static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
	.count = ARRAY_SIZE(rates),
	.list = rates,
	.mask = 0,
};

static unsigned int channels[] = {
  2, 4, 6
};

static struct snd_pcm_hw_constraint_list hw_constraints_channels = {
	.count = ARRAY_SIZE(channels),
	.list = channels,
	.mask = 0,
};

/*
 *  Sample rate routines
 */

static unsigned short snd_fm801_rate_bits(unsigned int rate)
{
	unsigned int idx;

	for (idx = 0; idx < ARRAY_SIZE(rates); idx++)
		if (rates[idx] == rate)
			return idx;
	snd_BUG();
	return ARRAY_SIZE(rates) - 1;
}

/*
 *  PCM part
 */

static int snd_fm801_playback_trigger(struct snd_pcm_substream *substream,
				      int cmd)
{
	struct fm801 *chip = snd_pcm_substream_chip(substream);

	spin_lock(&chip->reg_lock);
	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
		chip->ply_ctrl &= ~(FM801_BUF1_LAST |
				     FM801_BUF2_LAST |
				     FM801_PAUSE);
		chip->ply_ctrl |= FM801_START |
				   FM801_IMMED_STOP;
		break;
	case SNDRV_PCM_TRIGGER_STOP:
		chip->ply_ctrl &= ~(FM801_START | FM801_PAUSE);
		break;
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
	case SNDRV_PCM_TRIGGER_SUSPEND:
		chip->ply_ctrl |= FM801_PAUSE;
		break;
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
	case SNDRV_PCM_TRIGGER_RESUME:
		chip->ply_ctrl &= ~FM801_PAUSE;
		break;
	default:
		spin_unlock(&chip->reg_lock);
		snd_BUG();
		return -EINVAL;
	}
	outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL));
	spin_unlock(&chip->reg_lock);
	return 0;
}

static int snd_fm801_capture_trigger(struct snd_pcm_substream *substream,
				     int cmd)
{
	struct fm801 *chip = snd_pcm_substream_chip(substream);

	spin_lock(&chip->reg_lock);
	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
		chip->cap_ctrl &= ~(FM801_BUF1_LAST |
				     FM801_BUF2_LAST |
				     FM801_PAUSE);
		chip->cap_ctrl |= FM801_START |
				   FM801_IMMED_STOP;
		break;
	case SNDRV_PCM_TRIGGER_STOP:
		chip->cap_ctrl &= ~(FM801_START | FM801_PAUSE);
		break;
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
	case SNDRV_PCM_TRIGGER_SUSPEND:
		chip->cap_ctrl |= FM801_PAUSE;
		break;
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
	case SNDRV_PCM_TRIGGER_RESUME:
		chip->cap_ctrl &= ~FM801_PAUSE;
		break;
	default:
		spin_unlock(&chip->reg_lock);
		snd_BUG();
		return -EINVAL;
	}
	outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL));
	spin_unlock(&chip->reg_lock);
	return 0;
}

static int snd_fm801_hw_params(struct snd_pcm_substream *substream,
			       struct snd_pcm_hw_params *hw_params)
{
	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
}

static int snd_fm801_hw_free(struct snd_pcm_substream *substream)
{
	return snd_pcm_lib_free_pages(substream);
}

static int snd_fm801_playback_prepare(struct snd_pcm_substream *substream)
{
	struct fm801 *chip = snd_pcm_substream_chip(substream);
	struct snd_pcm_runtime *runtime = substream->runtime;

	chip->ply_size = snd_pcm_lib_buffer_bytes(substream);
	chip->ply_count = snd_pcm_lib_period_bytes(substream);
	spin_lock_irq(&chip->reg_lock);
	chip->ply_ctrl &= ~(FM801_START | FM801_16BIT |
			     FM801_STEREO | FM801_RATE_MASK |
			     FM801_CHANNELS_MASK);
	if (snd_pcm_format_width(runtime->format) == 16)
		chip->ply_ctrl |= FM801_16BIT;
	if (runtime->channels > 1) {
		chip->ply_ctrl |= FM801_STEREO;
		if (runtime->channels == 4)
			chip->ply_ctrl |= FM801_CHANNELS_4;
		else if (runtime->channels == 6)
			chip->ply_ctrl |= FM801_CHANNELS_6;
	}
	chip->ply_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT;
	chip->ply_buf = 0;
	outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL));
	outw(chip->ply_count - 1, FM801_REG(chip, PLY_COUNT));
	chip->ply_buffer = runtime->dma_addr;
	chip->ply_pos = 0;
	outl(chip->ply_buffer, FM801_REG(chip, PLY_BUF1));
	outl(chip->ply_buffer + (chip->ply_count % chip->ply_size), FM801_REG(chip, PLY_BUF2));
	spin_unlock_irq(&chip->reg_lock);
	return 0;
}

static int snd_fm801_capture_prepare(struct snd_pcm_substream *substream)
{
	struct fm801 *chip = snd_pcm_substream_chip(substream);
	struct snd_pcm_runtime *runtime = substream->runtime;

	chip->cap_size = snd_pcm_lib_buffer_bytes(substream);
	chip->cap_count = snd_pcm_lib_period_bytes(substream);
	spin_lock_irq(&chip->reg_lock);
	chip->cap_ctrl &= ~(FM801_START | FM801_16BIT |
			     FM801_STEREO | FM801_RATE_MASK);
	if (snd_pcm_format_width(runtime->format) == 16)
		chip->cap_ctrl |= FM801_16BIT;
	if (runtime->channels > 1)
		chip->cap_ctrl |= FM801_STEREO;
	chip->cap_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT;
	chip->cap_buf = 0;
	outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL));
	outw(chip->cap_count - 1, FM801_REG(chip, CAP_COUNT));
	chip->cap_buffer = runtime->dma_addr;
	chip->cap_pos = 0;
	outl(chip->cap_buffer, FM801_REG(chip, CAP_BUF1));
	outl(chip->cap_buffer + (chip->cap_count % chip->cap_size), FM801_REG(chip, CAP_BUF2));
	spin_unlock_irq(&chip->reg_lock);
	return 0;
}

static snd_pcm_uframes_t snd_fm801_playback_pointer(struct snd_pcm_substream *substream)
{
	struct fm801 *chip = snd_pcm_substream_chip(substream);
	size_t ptr;

	if (!(chip->ply_ctrl & FM801_START))
		return 0;
	spin_lock(&chip->reg_lock);
	ptr = chip->ply_pos + (chip->ply_count - 1) - inw(FM801_REG(chip, PLY_COUNT));
	if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_PLAYBACK) {
		ptr += chip->ply_count;
		ptr %= chip->ply_size;
	}
	spin_unlock(&chip->reg_lock);
	return bytes_to_frames(substream->runtime, ptr);
}

static snd_pcm_uframes_t snd_fm801_capture_pointer(struct snd_pcm_substream *substream)
{
	struct fm801 *chip = snd_pcm_substream_chip(substream);
	size_t ptr;

	if (!(chip->cap_ctrl & FM801_START))
		return 0;
	spin_lock(&chip->reg_lock);
	ptr = chip->cap_pos + (chip->cap_count - 1) - inw(FM801_REG(chip, CAP_COUNT));
	if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_CAPTURE) {
		ptr += chip->cap_count;
		ptr %= chip->cap_size;
	}
	spin_unlock(&chip->reg_lock);
	return bytes_to_frames(substream->runtime, ptr);
}

static irqreturn_t snd_fm801_interrupt(int irq, void *dev_id)
{
	struct fm801 *chip = dev_id;
	unsigned short status;
	unsigned int tmp;

	status = inw(FM801_REG(chip, IRQ_STATUS));
	status &= FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU|FM801_IRQ_VOLUME;
	if (! status)
		return IRQ_NONE;
	/* ack first */
	outw(status, FM801_REG(chip, IRQ_STATUS));
	if (chip->pcm && (status & FM801_IRQ_PLAYBACK) && chip->playback_substream) {
		spin_lock(&chip->reg_lock);
		chip->ply_buf++;
		chip->ply_pos += chip->ply_count;
		chip->ply_pos %= chip->ply_size;
		tmp = chip->ply_pos + chip->ply_count;
		tmp %= chip->ply_size;
		outl(chip->ply_buffer + tmp,
				(chip->ply_buf & 1) ?
					FM801_REG(chip, PLY_BUF1) :
					FM801_REG(chip, PLY_BUF2));
		spin_unlock(&chip->reg_lock);
		snd_pcm_period_elapsed(chip->playback_substream);
	}
	if (chip->pcm && (status & FM801_IRQ_CAPTURE) && chip->capture_substream) {
		spin_lock(&chip->reg_lock);
		chip->cap_buf++;
		chip->cap_pos += chip->cap_count;
		chip->cap_pos %= chip->cap_size;
		tmp = chip->cap_pos + chip->cap_count;
		tmp %= chip->cap_size;
		outl(chip->cap_buffer + tmp,
				(chip->cap_buf & 1) ?
					FM801_REG(chip, CAP_BUF1) :
					FM801_REG(chip, CAP_BUF2));
		spin_unlock(&chip->reg_lock);
		snd_pcm_period_elapsed(chip->capture_substream);
	}
	if (chip->rmidi && (status & FM801_IRQ_MPU))
		snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
	if (status & FM801_IRQ_VOLUME)
		;/* TODO */

	return IRQ_HANDLED;
}

static struct snd_pcm_hardware snd_fm801_playback =
{
	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |
				 SNDRV_PCM_INFO_MMAP_VALID),
	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
	.rate_min =		5500,
	.rate_max =		48000,
	.channels_min =		1,
	.channels_max =		2,
	.buffer_bytes_max =	(128*1024),
	.period_bytes_min =	64,
	.period_bytes_max =	(128*1024),
	.periods_min =		1,
	.periods_max =		1024,
	.fifo_size =		0,
};

static struct snd_pcm_hardware snd_fm801_capture =
{
	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |
				 SNDRV_PCM_INFO_MMAP_VALID),
	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
	.rate_min =		5500,
	.rate_max =		48000,
	.channels_min =		1,
	.channels_max =		2,
	.buffer_bytes_max =	(128*1024),
	.period_bytes_min =	64,
	.period_bytes_max =	(128*1024),
	.periods_min =		1,
	.periods_max =		1024,
	.fifo_size =		0,
};

static int snd_fm801_playback_open(struct snd_pcm_substream *substream)
{
	struct fm801 *chip = snd_pcm_substream_chip(substream);
	struct snd_pcm_runtime *runtime = substream->runtime;
	int err;

	chip->playback_substream = substream;
	runtime->hw = snd_fm801_playback;
	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
				   &hw_constraints_rates);
	if (chip->multichannel) {
		runtime->hw.channels_max = 6;
		snd_pcm_hw_constraint_list(runtime, 0,
					   SNDRV_PCM_HW_PARAM_CHANNELS,
					   &hw_constraints_channels);
	}
	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
		return err;
	return 0;
}

static int snd_fm801_capture_open(struct snd_pcm_substream *substream)
{
	struct fm801 *chip = snd_pcm_substream_chip(substream);
	struct snd_pcm_runtime *runtime = substream->runtime;
	int err;

	chip->capture_substream = substream;
	runtime->hw = snd_fm801_capture;
	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
				   &hw_constraints_rates);
	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
		return err;
	return 0;
}

static int snd_fm801_playback_close(struct snd_pcm_substream *substream)
{
	struct fm801 *chip = snd_pcm_substream_chip(substream);

	chip->playback_substream = NULL;
	return 0;
}

static int snd_fm801_capture_close(struct snd_pcm_substream *substream)
{
	struct fm801 *chip = snd_pcm_substream_chip(substream);

	chip->capture_substream = NULL;
	return 0;
}

static struct snd_pcm_ops snd_fm801_playback_ops = {
	.open =		snd_fm801_playback_open,
	.close =	snd_fm801_playback_close,
	.ioctl =	snd_pcm_lib_ioctl,
	.hw_params =	snd_fm801_hw_params,
	.hw_free =	snd_fm801_hw_free,
	.prepare =	snd_fm801_playback_prepare,
	.trigger =	snd_fm801_playback_trigger,
	.pointer =	snd_fm801_playback_pointer,
};

static struct snd_pcm_ops snd_fm801_capture_ops = {
	.open =		snd_fm801_capture_open,
	.close =	snd_fm801_capture_close,
	.ioctl =	snd_pcm_lib_ioctl,
	.hw_params =	snd_fm801_hw_params,
	.hw_free =	snd_fm801_hw_free,
	.prepare =	snd_fm801_capture_prepare,
	.trigger =	snd_fm801_capture_trigger,
	.pointer =	snd_fm801_capture_pointer,
};

static int __devinit snd_fm801_pcm(struct fm801 *chip, int device, struct snd_pcm ** rpcm)
{
	struct snd_pcm *pcm;
	int err;

	if (rpcm)
		*rpcm = NULL;
	if ((err = snd_pcm_new(chip->card, "FM801", device, 1, 1, &pcm)) < 0)
		return err;

	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_fm801_playback_ops);
	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_fm801_capture_ops);

	pcm->private_data = chip;
	pcm->info_flags = 0;
	strcpy(pcm->name, "FM801");
	chip->pcm = pcm;

	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
					      snd_dma_pci_data(chip->pci),
					      chip->multichannel ? 128*1024 : 64*1024, 128*1024);

	if (rpcm)
		*rpcm = pcm;
	return 0;
}

/*
 *  TEA5757 radio
 */

#ifdef TEA575X_RADIO

/* 256PCS GPIO numbers */
#define TEA_256PCS_DATA			1
#define TEA_256PCS_WRITE_ENABLE		2	/* inverted */
#define TEA_256PCS_BUS_CLOCK		3

static void snd_fm801_tea575x_256pcs_write(struct snd_tea575x *tea, unsigned int val)
{
	struct fm801 *chip = tea->private_data;
	unsigned short reg;
	int i = 25;

	spin_lock_irq(&chip->reg_lock);
	reg = inw(FM801_REG(chip, GPIO_CTRL));
	/* use GPIO lines and set write enable bit */
	reg |= FM801_GPIO_GS(TEA_256PCS_DATA) |
	       FM801_GPIO_GS(TEA_256PCS_WRITE_ENABLE) |
	       FM801_GPIO_GS(TEA_256PCS_BUS_CLOCK);
	/* all of lines are in the write direction */
	/* clear data and clock lines */
	reg &= ~(FM801_GPIO_GD(TEA_256PCS_DATA) |
	         FM801_GPIO_GD(TEA_256PCS_WRITE_ENABLE) |
	         FM801_GPIO_GD(TEA_256PCS_BUS_CLOCK) |
	         FM801_GPIO_GP(TEA_256PCS_DATA) |
	         FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK) |
		 FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE));
	outw(reg, FM801_REG(chip, GPIO_CTRL));
	udelay(1);

	while (i--) {
		if (val & (1 << i))
			reg |= FM801_GPIO_GP(TEA_256PCS_DATA);
		else
			reg &= ~FM801_GPIO_GP(TEA_256PCS_DATA);
		outw(reg, FM801_REG(chip, GPIO_CTRL));
		udelay(1);
		reg |= FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
		outw(reg, FM801_REG(chip, GPIO_CTRL));
		reg &= ~FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
		outw(reg, FM801_REG(chip, GPIO_CTRL));
		udelay(1);
	}

	/* and reset the write enable bit */
	reg |= FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE) |
	       FM801_GPIO_GP(TEA_256PCS_DATA);
	outw(reg, FM801_REG(chip, GPIO_CTRL));
	spin_unlock_irq(&chip->reg_lock);
}

static unsigned int snd_fm801_tea575x_256pcs_read(struct snd_tea575x *tea)
{
	struct fm801 *chip = tea->private_data;
	unsigned short reg;
	unsigned int val = 0;
	int i;

	spin_lock_irq(&chip->reg_lock);
	reg = inw(FM801_REG(chip, GPIO_CTRL));
	/* use GPIO lines, set data direction to input */
	reg |= FM801_GPIO_GS(TEA_256PCS_DATA) |
	       FM801_GPIO_GS(TEA_256PCS_WRITE_ENABLE) |
	       FM801_GPIO_GS(TEA_256PCS_BUS_CLOCK) |
	       FM801_GPIO_GD(TEA_256PCS_DATA) |
	       FM801_GPIO_GP(TEA_256PCS_DATA) |
	       FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE);
	/* all of lines are in the write direction, except data */
	/* clear data, write enable and clock lines */
	reg &= ~(FM801_GPIO_GD(TEA_256PCS_WRITE_ENABLE) |
	         FM801_GPIO_GD(TEA_256PCS_BUS_CLOCK) |
	         FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK));

	for (i = 0; i < 24; i++) {
		reg &= ~FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
		outw(reg, FM801_REG(chip, GPIO_CTRL));
		udelay(1);
		reg |= FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
		outw(reg, FM801_REG(chip, GPIO_CTRL));
		udelay(1);
		val <<= 1;
		if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_256PCS_DATA))
			val |= 1;
	}

	spin_unlock_irq(&chip->reg_lock);

	return val;
}

/* 256PCPR GPIO numbers */
#define TEA_256PCPR_BUS_CLOCK		0
#define TEA_256PCPR_DATA		1
#define TEA_256PCPR_WRITE_ENABLE	2	/* inverted */

static void snd_fm801_tea575x_256pcpr_write(struct snd_tea575x *tea, unsigned int val)
{
	struct fm801 *chip = tea->private_data;
	unsigned short reg;
	int i = 25;

	spin_lock_irq(&chip->reg_lock);
	reg = inw(FM801_REG(chip, GPIO_CTRL));
	/* use GPIO lines and set write enable bit */
	reg |= FM801_GPIO_GS(TEA_256PCPR_DATA) |
	       FM801_GPIO_GS(TEA_256PCPR_WRITE_ENABLE) |
	       FM801_GPIO_GS(TEA_256PCPR_BUS_CLOCK);
	/* all of lines are in the write direction */
	/* clear data and clock lines */
	reg &= ~(FM801_GPIO_GD(TEA_256PCPR_DATA) |
	         FM801_GPIO_GD(TEA_256PCPR_WRITE_ENABLE) |
	         FM801_GPIO_GD(TEA_256PCPR_BUS_CLOCK) |
	         FM801_GPIO_GP(TEA_256PCPR_DATA) |
	         FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK) |
		 FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE));
	outw(reg, FM801_REG(chip, GPIO_CTRL));
	udelay(1);

	while (i--) {
		if (val & (1 << i))
			reg |= FM801_GPIO_GP(TEA_256PCPR_DATA);
		else
			reg &= ~FM801_GPIO_GP(TEA_256PCPR_DATA);
		outw(reg, FM801_REG(chip, GPIO_CTRL));
		udelay(1);
		reg |= FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
		outw(reg, FM801_REG(chip, GPIO_CTRL));
		reg &= ~FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
		outw(reg, FM801_REG(chip, GPIO_CTRL));
		udelay(1);
	}

	/* and reset the write enable bit */
	reg |= FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE) |
	       FM801_GPIO_GP(TEA_256PCPR_DATA);
	outw(reg, FM801_REG(chip, GPIO_CTRL));
	spin_unlock_irq(&chip->reg_lock);
}

static unsigned int snd_fm801_tea575x_256pcpr_read(struct snd_tea575x *tea)
{
	struct fm801 *chip = tea->private_data;
	unsigned short reg;
	unsigned int val = 0;
	int i;

	spin_lock_irq(&chip->reg_lock);
	reg = inw(FM801_REG(chip, GPIO_CTRL));
	/* use GPIO lines, set data direction to input */
	reg |= FM801_GPIO_GS(TEA_256PCPR_DATA) |
	       FM801_GPIO_GS(TEA_256PCPR_WRITE_ENABLE) |
	       FM801_GPIO_GS(TEA_256PCPR_BUS_CLOCK) |
	       FM801_GPIO_GD(TEA_256PCPR_DATA) |
	       FM801_GPIO_GP(TEA_256PCPR_DATA) |
	       FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE);
	/* all of lines are in the write direction, except data */
	/* clear data, write enable and clock lines */
	reg &= ~(FM801_GPIO_GD(TEA_256PCPR_WRITE_ENABLE) |
	         FM801_GPIO_GD(TEA_256PCPR_BUS_CLOCK) |
	         FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK));

	for (i = 0; i < 24; i++) {
		reg &= ~FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
		outw(reg, FM801_REG(chip, GPIO_CTRL));
		udelay(1);
		reg |= FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
		outw(reg, FM801_REG(chip, GPIO_CTRL));
		udelay(1);
		val <<= 1;
		if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_256PCPR_DATA))
			val |= 1;
	}

	spin_unlock_irq(&chip->reg_lock);

	return val;
}

/* 64PCR GPIO numbers */
#define TEA_64PCR_BUS_CLOCK		0
#define TEA_64PCR_WRITE_ENABLE		1	/* inverted */
#define TEA_64PCR_DATA			2

static void snd_fm801_tea575x_64pcr_write(struct snd_tea575x *tea, unsigned int val)
{
	struct fm801 *chip = tea->private_data;
	unsigned short reg;
	int i = 25;

	spin_lock_irq(&chip->reg_lock);
	reg = inw(FM801_REG(chip, GPIO_CTRL));
	/* use GPIO lines and set write enable bit */
	reg |= FM801_GPIO_GS(TEA_64PCR_DATA) |
	       FM801_GPIO_GS(TEA_64PCR_WRITE_ENABLE) |
	       FM801_GPIO_GS(TEA_64PCR_BUS_CLOCK);
	/* all of lines are in the write direction */
	/* clear data and clock lines */
	reg &= ~(FM801_GPIO_GD(TEA_64PCR_DATA) |
	         FM801_GPIO_GD(TEA_64PCR_WRITE_ENABLE) |
	         FM801_GPIO_GD(TEA_64PCR_BUS_CLOCK) |
	         FM801_GPIO_GP(TEA_64PCR_DATA) |
	         FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK) |
		 FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE));
	outw(reg, FM801_REG(chip, GPIO_CTRL));
	udelay(1);

	while (i--) {
		if (val & (1 << i))
			reg |= FM801_GPIO_GP(TEA_64PCR_DATA);
		else
			reg &= ~FM801_GPIO_GP(TEA_64PCR_DATA);
		outw(reg, FM801_REG(chip, GPIO_CTRL));
		udelay(1);
		reg |= FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
		outw(reg, FM801_REG(chip, GPIO_CTRL));
		reg &= ~FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
		outw(reg, FM801_REG(chip, GPIO_CTRL));
		udelay(1);
	}

	/* and reset the write enable bit */
	reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE) |
	       FM801_GPIO_GP(TEA_64PCR_DATA);
	outw(reg, FM801_REG(chip, GPIO_CTRL));
	spin_unlock_irq(&chip->reg_lock);
}

static unsigned int snd_fm801_tea575x_64pcr_read(struct snd_tea575x *tea)
{
	struct fm801 *chip = tea->private_data;
	unsigned short reg;
	unsigned int val = 0;
	int i;

	spin_lock_irq(&chip->reg_lock);
	reg = inw(FM801_REG(chip, GPIO_CTRL));
	/* use GPIO lines, set data direction to input */
	reg |= FM801_GPIO_GS(TEA_64PCR_DATA) |
	       FM801_GPIO_GS(TEA_64PCR_WRITE_ENABLE) |
	       FM801_GPIO_GS(TEA_64PCR_BUS_CLOCK) |
	       FM801_GPIO_GD(TEA_64PCR_DATA) |
	       FM801_GPIO_GP(TEA_64PCR_DATA) |
	       FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
	/* all of lines are in the write direction, except data */
	/* clear data, write enable and clock lines */
	reg &= ~(FM801_GPIO_GD(TEA_64PCR_WRITE_ENABLE) |
	         FM801_GPIO_GD(TEA_64PCR_BUS_CLOCK) |
	         FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK));

	for (i = 0; i < 24; i++) {
		reg &= ~FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
		outw(reg, FM801_REG(chip, GPIO_CTRL));
		udelay(1);
		reg |= FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
		outw(reg, FM801_REG(chip, GPIO_CTRL));
		udelay(1);
		val <<= 1;
		if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_64PCR_DATA))
			val |= 1;
	}

	spin_unlock_irq(&chip->reg_lock);

	return val;
}

static void snd_fm801_tea575x_64pcr_mute(struct snd_tea575x *tea,
					  unsigned int mute)
{
	struct fm801 *chip = tea->private_data;
	unsigned short reg;

	spin_lock_irq(&chip->reg_lock);

	reg = inw(FM801_REG(chip, GPIO_CTRL));
	if (mute)
		/* 0xf800 (mute) */
		reg &= ~FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
	else
		/* 0xf802 (unmute) */
		reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
	outw(reg, FM801_REG(chip, GPIO_CTRL));
	udelay(1);

	spin_unlock_irq(&chip->reg_lock);
}

static struct snd_tea575x_ops snd_fm801_tea_ops[3] = {
	{
		/* 1 = MediaForte 256-PCS */
		.write = snd_fm801_tea575x_256pcs_write,
		.read = snd_fm801_tea575x_256pcs_read,
	},
	{
		/* 2 = MediaForte 256-PCPR */
		.write = snd_fm801_tea575x_256pcpr_write,
		.read = snd_fm801_tea575x_256pcpr_read,
	},
	{
		/* 3 = MediaForte 64-PCR */
		.write = snd_fm801_tea575x_64pcr_write,
		.read = snd_fm801_tea575x_64pcr_read,
		.mute = snd_fm801_tea575x_64pcr_mute,
	}
};
#endif

/*
 *  Mixer routines
 */

#define FM801_SINGLE(xname, reg, shift, mask, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_fm801_info_single, \
  .get = snd_fm801_get_single, .put = snd_fm801_put_single, \
  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }

static int snd_fm801_info_single(struct snd_kcontrol *kcontrol,
				 struct snd_ctl_elem_info *uinfo)
{
	int mask = (kcontrol->private_value >> 16) & 0xff;

	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
	uinfo->count = 1;
	uinfo->value.integer.min = 0;
	uinfo->value.integer.max = mask;
	return 0;
}

static int snd_fm801_get_single(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	struct fm801 *chip = snd_kcontrol_chip(kcontrol);
	int reg = kcontrol->private_value & 0xff;
	int shift = (kcontrol->private_value >> 8) & 0xff;
	int mask = (kcontrol->private_value >> 16) & 0xff;
	int invert = (kcontrol->private_value >> 24) & 0xff;

	ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift) & mask;
	if (invert)
		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
	return 0;
}

static int snd_fm801_put_single(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	struct fm801 *chip = snd_kcontrol_chip(kcontrol);
	int reg = kcontrol->private_value & 0xff;
	int shift = (kcontrol->private_value >> 8) & 0xff;
	int mask = (kcontrol->private_value >> 16) & 0xff;
	int invert = (kcontrol->private_value >> 24) & 0xff;
	unsigned short val;

	val = (ucontrol->value.integer.value[0] & mask);
	if (invert)
		val = mask - val;
	return snd_fm801_update_bits(chip, reg, mask << shift, val << shift);
}

#define FM801_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_fm801_info_double, \
  .get = snd_fm801_get_double, .put = snd_fm801_put_double, \
  .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) }
#define FM801_DOUBLE_TLV(xname, reg, shift_left, shift_right, mask, invert, xtlv) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
  .name = xname, .info = snd_fm801_info_double, \
  .get = snd_fm801_get_double, .put = snd_fm801_put_double, \
  .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24), \
  .tlv = { .p = (xtlv) } }

static int snd_fm801_info_double(struct snd_kcontrol *kcontrol,
				 struct snd_ctl_elem_info *uinfo)
{
	int mask = (kcontrol->private_value >> 16) & 0xff;

	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
	uinfo->count = 2;
	uinfo->value.integer.min = 0;
	uinfo->value.integer.max = mask;
	return 0;
}

static int snd_fm801_get_double(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	struct fm801 *chip = snd_kcontrol_chip(kcontrol);
        int reg = kcontrol->private_value & 0xff;
	int shift_left = (kcontrol->private_value >> 8) & 0x0f;
	int shift_right = (kcontrol->private_value >> 12) & 0x0f;
	int mask = (kcontrol->private_value >> 16) & 0xff;
	int invert = (kcontrol->private_value >> 24) & 0xff;

	spin_lock_irq(&chip->reg_lock);
	ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift_left) & mask;
	ucontrol->value.integer.value[1] = (inw(chip->port + reg) >> shift_right) & mask;
	spin_unlock_irq(&chip->reg_lock);
	if (invert) {
		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
	}
	return 0;
}

static int snd_fm801_put_double(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	struct fm801 *chip = snd_kcontrol_chip(kcontrol);
	int reg = kcontrol->private_value & 0xff;
	int shift_left = (kcontrol->private_value >> 8) & 0x0f;
	int shift_right = (kcontrol->private_value >> 12) & 0x0f;
	int mask = (kcontrol->private_value >> 16) & 0xff;
	int invert = (kcontrol->private_value >> 24) & 0xff;
	unsigned short val1, val2;

	val1 = ucontrol->value.integer.value[0] & mask;
	val2 = ucontrol->value.integer.value[1] & mask;
	if (invert) {
		val1 = mask - val1;
		val2 = mask - val2;
	}
	return snd_fm801_update_bits(chip, reg,
				     (mask << shift_left) | (mask << shift_right),
				     (val1 << shift_left ) | (val2 << shift_right));
}

static int snd_fm801_info_mux(struct snd_kcontrol *kcontrol,
			      struct snd_ctl_elem_info *uinfo)
{
	static char *texts[5] = {
		"AC97 Primary", "FM", "I2S", "PCM", "AC97 Secondary"
	};

	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
	uinfo->count = 1;
	uinfo->value.enumerated.items = 5;
	if (uinfo->value.enumerated.item > 4)
		uinfo->value.enumerated.item = 4;
	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
	return 0;
}

static int snd_fm801_get_mux(struct snd_kcontrol *kcontrol,
			     struct snd_ctl_elem_value *ucontrol)
{
	struct fm801 *chip = snd_kcontrol_chip(kcontrol);
        unsigned short val;

	val = inw(FM801_REG(chip, REC_SRC)) & 7;
	if (val > 4)
		val = 4;
        ucontrol->value.enumerated.item[0] = val;
        return 0;
}

static int snd_fm801_put_mux(struct snd_kcontrol *kcontrol,
			     struct snd_ctl_elem_value *ucontrol)
{
	struct fm801 *chip = snd_kcontrol_chip(kcontrol);
        unsigned short val;

        if ((val = ucontrol->value.enumerated.item[0]) > 4)
                return -EINVAL;
	return snd_fm801_update_bits(chip, FM801_REC_SRC, 7, val);
}

static const DECLARE_TLV_DB_SCALE(db_scale_dsp, -3450, 150, 0);

#define FM801_CONTROLS ARRAY_SIZE(snd_fm801_controls)

static struct snd_kcontrol_new snd_fm801_controls[] __devinitdata = {
FM801_DOUBLE_TLV("Wave Playback Volume", FM801_PCM_VOL, 0, 8, 31, 1,
		 db_scale_dsp),
FM801_SINGLE("Wave Playback Switch", FM801_PCM_VOL, 15, 1, 1),
FM801_DOUBLE_TLV("I2S Playback Volume", FM801_I2S_VOL, 0, 8, 31, 1,
		 db_scale_dsp),
FM801_SINGLE("I2S Playback Switch", FM801_I2S_VOL, 15, 1, 1),
FM801_DOUBLE_TLV("FM Playback Volume", FM801_FM_VOL, 0, 8, 31, 1,
		 db_scale_dsp),
FM801_SINGLE("FM Playback Switch", FM801_FM_VOL, 15, 1, 1),
{
	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
	.name = "Digital Capture Source",
	.info = snd_fm801_info_mux,
	.get = snd_fm801_get_mux,
	.put = snd_fm801_put_mux,
}
};

#define FM801_CONTROLS_MULTI ARRAY_SIZE(snd_fm801_controls_multi)

static struct snd_kcontrol_new snd_fm801_controls_multi[] __devinitdata = {
FM801_SINGLE("AC97 2ch->4ch Copy Switch", FM801_CODEC_CTRL, 7, 1, 0),
FM801_SINGLE("AC97 18-bit Switch", FM801_CODEC_CTRL, 10, 1, 0),
FM801_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), FM801_I2S_MODE, 8, 1, 0),
FM801_SINGLE(SNDRV_CTL_NAME_IEC958("Raw Data ",PLAYBACK,SWITCH), FM801_I2S_MODE, 9, 1, 0),
FM801_SINGLE(SNDRV_CTL_NAME_IEC958("Raw Data ",CAPTURE,SWITCH), FM801_I2S_MODE, 10, 1, 0),
FM801_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), FM801_GEN_CTRL, 2, 1, 0),
};

static void snd_fm801_mixer_free_ac97_bus(struct snd_ac97_bus *bus)
{
	struct fm801 *chip = bus->private_data;
	chip->ac97_bus = NULL;
}

static void snd_fm801_mixer_free_ac97(struct snd_ac97 *ac97)
{
	struct fm801 *chip = ac97->private_data;
	if (ac97->num == 0) {
		chip->ac97 = NULL;
	} else {
		chip->ac97_sec = NULL;
	}
}

static int __devinit snd_fm801_mixer(struct fm801 *chip)
{
	struct snd_ac97_template ac97;
	unsigned int i;
	int err;
	static struct snd_ac97_bus_ops ops = {
		.write = snd_fm801_codec_write,
		.read = snd_fm801_codec_read,
	};

	if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0)
		return err;
	chip->ac97_bus->private_free = snd_fm801_mixer_free_ac97_bus;

	memset(&ac97, 0, sizeof(ac97));
	ac97.private_data = chip;
	ac97.private_free = snd_fm801_mixer_free_ac97;
	if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0)
		return err;
	if (chip->secondary) {
		ac97.num = 1;
		ac97.addr = chip->secondary_addr;
		if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97_sec)) < 0)
			return err;
	}
	for (i = 0; i < FM801_CONTROLS; i++)
		snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls[i], chip));
	if (chip->multichannel) {
		for (i = 0; i < FM801_CONTROLS_MULTI; i++)
			snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls_multi[i], chip));
	}
	return 0;
}

/*
 *  initialization routines
 */

static int wait_for_codec(struct fm801 *chip, unsigned int codec_id,
			  unsigned short reg, unsigned long waits)
{
	unsigned long timeout = jiffies + waits;

	outw(FM801_AC97_READ | (codec_id << FM801_AC97_ADDR_SHIFT) | reg,
	     FM801_REG(chip, AC97_CMD));
	udelay(5);
	do {
		if ((inw(FM801_REG(chip, AC97_CMD)) & (FM801_AC97_VALID|FM801_AC97_BUSY))
		    == FM801_AC97_VALID)
			return 0;
		schedule_timeout_uninterruptible(1);
	} while (time_after(timeout, jiffies));
	return -EIO;
}

static int snd_fm801_chip_init(struct fm801 *chip, int resume)
{
	unsigned short cmdw;

	if (chip->tea575x_tuner & TUNER_ONLY)
		goto __ac97_ok;

	/* codec cold reset + AC'97 warm reset */
	outw((1<<5) | (1<<6), FM801_REG(chip, CODEC_CTRL));
	inw(FM801_REG(chip, CODEC_CTRL)); /* flush posting data */
	udelay(100);
	outw(0, FM801_REG(chip, CODEC_CTRL));

	if (wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750)) < 0)
		if (!resume) {
			snd_printk(KERN_INFO "Primary AC'97 codec not found, "
					    "assume SF64-PCR (tuner-only)\n");
			chip->tea575x_tuner = 3 | TUNER_ONLY;
			goto __ac97_ok;
		}

	if (chip->multichannel) {
		if (chip->secondary_addr) {
			wait_for_codec(chip, chip->secondary_addr,
				       AC97_VENDOR_ID1, msecs_to_jiffies(50));
		} else {
			/* my card has the secondary codec */
			/* at address #3, so the loop is inverted */
			int i;
			for (i = 3; i > 0; i--) {
				if (!wait_for_codec(chip, i, AC97_VENDOR_ID1,
						     msecs_to_jiffies(50))) {
					cmdw = inw(FM801_REG(chip, AC97_DATA));
					if (cmdw != 0xffff && cmdw != 0) {
						chip->secondary = 1;
						chip->secondary_addr = i;
						break;
					}
				}
			}
		}

		/* the recovery phase, it seems that probing for non-existing codec might */
		/* cause timeout problems */
		wait_for_codec(chip, 0, AC97_VENDOR_ID1, msecs_to_jiffies(750));
	}

      __ac97_ok:

	/* init volume */
	outw(0x0808, FM801_REG(chip, PCM_VOL));
	outw(0x9f1f, FM801_REG(chip, FM_VOL));
	outw(0x8808, FM801_REG(chip, I2S_VOL));

	/* I2S control - I2S mode */
	outw(0x0003, FM801_REG(chip, I2S_MODE));

	/* interrupt setup */
	cmdw = inw(FM801_REG(chip, IRQ_MASK));
	if (chip->irq < 0)
		cmdw |= 0x00c3;		/* mask everything, no PCM nor MPU */
	else
		cmdw &= ~0x0083;	/* unmask MPU, PLAYBACK & CAPTURE */
	outw(cmdw, FM801_REG(chip, IRQ_MASK));

	/* interrupt clear */
	outw(FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU, FM801_REG(chip, IRQ_STATUS));

	return 0;
}

static int snd_fm801_free(struct fm801 *chip)
{
	unsigned short cmdw;

	if (chip->irq < 0)
		goto __end_hw;

	/* interrupt setup - mask everything */
	cmdw = inw(FM801_REG(chip, IRQ_MASK));
	cmdw |= 0x00c3;
	outw(cmdw, FM801_REG(chip, IRQ_MASK));

      __end_hw:
#ifdef TEA575X_RADIO
	snd_tea575x_exit(&chip->tea);
#endif
	if (chip->irq >= 0)
		free_irq(chip->irq, chip);
	pci_release_regions(chip->pci);
	pci_disable_device(chip->pci);

	kfree(chip);
	return 0;
}

static int snd_fm801_dev_free(struct snd_device *device)
{
	struct fm801 *chip = device->device_data;
	return snd_fm801_free(chip);
}

static int __devinit snd_fm801_create(struct snd_card *card,
				      struct pci_dev * pci,
				      int tea575x_tuner,
				      struct fm801 ** rchip)
{
	struct fm801 *chip;
	int err;
	static struct snd_device_ops ops = {
		.dev_free =	snd_fm801_dev_free,
	};

	*rchip = NULL;
	if ((err = pci_enable_device(pci)) < 0)
		return err;
	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
	if (chip == NULL) {
		pci_disable_device(pci);
		return -ENOMEM;
	}
	spin_lock_init(&chip->reg_lock);
	chip->card = card;
	chip->pci = pci;
	chip->irq = -1;
	chip->tea575x_tuner = tea575x_tuner;
	if ((err = pci_request_regions(pci, "FM801")) < 0) {
		kfree(chip);
		pci_disable_device(pci);
		return err;
	}
	chip->port = pci_resource_start(pci, 0);
	if ((tea575x_tuner & TUNER_ONLY) == 0) {
		if (request_irq(pci->irq, snd_fm801_interrupt, IRQF_SHARED,
				"FM801", chip)) {
			snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->irq);
			snd_fm801_free(chip);
			return -EBUSY;
		}
		chip->irq = pci->irq;
		pci_set_master(pci);
	}

	if (pci->revision >= 0xb1)	/* FM801-AU */
		chip->multichannel = 1;

	snd_fm801_chip_init(chip, 0);
	/* init might set tuner access method */
	tea575x_tuner = chip->tea575x_tuner;

	if (chip->irq >= 0 && (tea575x_tuner & TUNER_ONLY)) {
		pci_clear_master(pci);
		free_irq(chip->irq, chip);
		chip->irq = -1;
	}

	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
		snd_fm801_free(chip);
		return err;
	}

	snd_card_set_dev(card, &pci->dev);

#ifdef TEA575X_RADIO
	if ((tea575x_tuner & TUNER_TYPE_MASK) > 0 &&
	    (tea575x_tuner & TUNER_TYPE_MASK) < 4) {
		chip->tea.dev_nr = tea575x_tuner >> 16;
		chip->tea.card = card;
		chip->tea.freq_fixup = 10700;
		chip->tea.private_data = chip;
		chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & TUNER_TYPE_MASK) - 1];
		snd_tea575x_init(&chip->tea);
	}
#endif

	*rchip = chip;
	return 0;
}

static int __devinit snd_card_fm801_probe(struct pci_dev *pci,
					  const struct pci_device_id *pci_id)
{
	static int dev;
	struct snd_card *card;
	struct fm801 *chip;
	struct snd_opl3 *opl3;
	int err;

        if (dev >= SNDRV_CARDS)
                return -ENODEV;
	if (!enable[dev]) {
		dev++;
		return -ENOENT;
	}

	err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
	if (err < 0)
		return err;
	if ((err = snd_fm801_create(card, pci, tea575x_tuner[dev], &chip)) < 0) {
		snd_card_free(card);
		return err;
	}
	card->private_data = chip;

	strcpy(card->driver, "FM801");
	strcpy(card->shortname, "ForteMedia FM801-");
	strcat(card->shortname, chip->multichannel ? "AU" : "AS");
	sprintf(card->longname, "%s at 0x%lx, irq %i",
		card->shortname, chip->port, chip->irq);

	if (chip->tea575x_tuner & TUNER_ONLY)
		goto __fm801_tuner_only;

	if ((err = snd_fm801_pcm(chip, 0, NULL)) < 0) {
		snd_card_free(card);
		return err;
	}
	if ((err = snd_fm801_mixer(chip)) < 0) {
		snd_card_free(card);
		return err;
	}
	if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801,
				       FM801_REG(chip, MPU401_DATA),
				       MPU401_INFO_INTEGRATED,
				       chip->irq, 0, &chip->rmidi)) < 0) {
		snd_card_free(card);
		return err;
	}
	if ((err = snd_opl3_create(card, FM801_REG(chip, OPL3_BANK0),
				   FM801_REG(chip, OPL3_BANK1),
				   OPL3_HW_OPL3_FM801, 1, &opl3)) < 0) {
		snd_card_free(card);
		return err;
	}
	if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
		snd_card_free(card);
		return err;
	}

      __fm801_tuner_only:
	if ((err = snd_card_register(card)) < 0) {
		snd_card_free(card);
		return err;
	}
	pci_set_drvdata(pci, card);
	dev++;
	return 0;
}

static void __devexit snd_card_fm801_remove(struct pci_dev *pci)
{
	snd_card_free(pci_get_drvdata(pci));
	pci_set_drvdata(pci, NULL);
}

#ifdef CONFIG_PM
static unsigned char saved_regs[] = {
	FM801_PCM_VOL, FM801_I2S_VOL, FM801_FM_VOL, FM801_REC_SRC,
	FM801_PLY_CTRL, FM801_PLY_COUNT, FM801_PLY_BUF1, FM801_PLY_BUF2,
	FM801_CAP_CTRL, FM801_CAP_COUNT, FM801_CAP_BUF1, FM801_CAP_BUF2,
	FM801_CODEC_CTRL, FM801_I2S_MODE, FM801_VOLUME, FM801_GEN_CTRL,
};

static int snd_fm801_suspend(struct pci_dev *pci, pm_message_t state)
{
	struct snd_card *card = pci_get_drvdata(pci);
	struct fm801 *chip = card->private_data;
	int i;

	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
	snd_pcm_suspend_all(chip->pcm);
	snd_ac97_suspend(chip->ac97);
	snd_ac97_suspend(chip->ac97_sec);
	for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
		chip->saved_regs[i] = inw(chip->port + saved_regs[i]);

	pci_disable_device(pci);
	pci_save_state(pci);
	pci_set_power_state(pci, pci_choose_state(pci, state));
	return 0;
}

static int snd_fm801_resume(struct pci_dev *pci)
{
	struct snd_card *card = pci_get_drvdata(pci);
	struct fm801 *chip = card->private_data;
	int i;

	pci_set_power_state(pci, PCI_D0);
	pci_restore_state(pci);
	if (pci_enable_device(pci) < 0) {
		printk(KERN_ERR "fm801: pci_enable_device failed, "
		       "disabling device\n");
		snd_card_disconnect(card);
		return -EIO;
	}
	pci_set_master(pci);

	snd_fm801_chip_init(chip, 1);
	snd_ac97_resume(chip->ac97);
	snd_ac97_resume(chip->ac97_sec);
	for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
		outw(chip->saved_regs[i], chip->port + saved_regs[i]);

	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
	return 0;
}
#endif

static struct pci_driver driver = {
	.name = "FM801",
	.id_table = snd_fm801_ids,
	.probe = snd_card_fm801_probe,
	.remove = __devexit_p(snd_card_fm801_remove),
#ifdef CONFIG_PM
	.suspend = snd_fm801_suspend,
	.resume = snd_fm801_resume,
#endif
};

static int __init alsa_card_fm801_init(void)
{
	return pci_register_driver(&driver);
}

static void __exit alsa_card_fm801_exit(void)
{
	pci_unregister_driver(&driver);
}

module_init(alsa_card_fm801_init)
module_exit(alsa_card_fm801_exit)


# Contributing to Cobra

Thank you so much for contributing to Cobra. We appreciate your time and help.
Here are some guidelines to help you get started.

## Code of Conduct

Be kind and respectful to the members of the community. Take time to educate
others who are seeking help. Harassment of any kind will not be tolerated.

## Questions

If you have questions regarding Cobra, feel free to ask it in the community
[#cobra Slack channel][cobra-slack]

## Filing a bug or feature

1. Before filing an issue, please check the existing issues to see if a
   similar one was already opened. If there is one already opened, feel free
   to comment on it.
1. If you believe you've found a bug, please provide detailed steps of
   reproduction, the version of Cobra and anything else you believe will be
   useful to help troubleshoot it (e.g. OS environment, environment variables,
   etc...). Also state the current behavior vs. the expected behavior.
1. If you'd like to see a feature or an enhancement please open an issue with
   a clear title and description of what the feature is and why it would be
   beneficial to the project and its users.

## Submitting changes

1. CLA: Upon submitting a Pull Request (PR), contributors will be prompted to
   sign a CLA. Please sign the CLA :slightly_smiling_face:
1. Tests: If you are submitting code, please ensure you have adequate tests
   for the feature. Tests can be run via `go test ./...` or `make test`.
1. Since this is golang project, ensure the new code is properly formatted to
   ensure code consistency. Run `make all`.

### Quick steps to contribute

1. Fork the project.
1. Download your fork to your PC (`git clone https://github.com/your_username/cobra && cd cobra`)
1. Create your feature branch (`git checkout -b my-new-feature`)
1. Make changes and run tests (`make test`)
1. Add them to staging (`git add .`)
1. Commit your changes (`git commit -m 'Add some feature'`)
1. Push to the branch (`git push origin my-new-feature`)
1. Create new pull request

<!-- Links -->
[cobra-slack]: https://gophers.slack.com/archives/CD3LP1199


 Error 0x4.
 Tipo di installazione errata. Può essere solamente Server, Agent o Local.


Title: Sysdig: 系统故障排查利器
Date: 2015-01-15 11:36
Author: toy
Category: Apps
Slug: sysdig

[Sysdig][s] 在操作系统层面进行监听，并将系统调用及系统事件等系统活动捕获下来，这使得它看起来极像面向系统的 tcpdump 或 Wireshark。如果你打算对系统中的异常故障进行排查，那么 Sysdig 将成为你解决问题得心应手的利器。

在 Linux 上，可使用以下命令来安装 Sysdig：

    :::bash
    curl -s https://s3.amazonaws.com/download.draios.com/stable/install-sysdig | sudo bash

这将把 Sysdig 安装到 rpm 或 deb 系的 Linux 系统。

**捕获系统活动**

实时捕获，结果打印到标准输出：

    :::bash
    sysdig

将捕获结果保存到文件 system.scap，方便稍后分析：

    :::bash
    sysdig -w system.scap

捕获指定的事件数 200 并保存到文件：

    :::bash
    sysdig -n 200 -w system.scap

读取已捕获的文件：

    :::bash
    sysdig -r system.scap

**捕获结果解释**

    :::bash
    (1) (2) (3) (4) (5) (6) (7) (8)
    1 10:54:50.462463956 0 sysdig (29043) > sysdigevent event_type=1
    event_data=0
    2 10:54:50.462603110 0 sysdig (29043) > sysdigevent event_type=1
    event_data=0
    3 10:54:50.462729565 0 sysdig (29043) > sysdigevent event_type=1
    event_data=0
    4 10:54:50.462859521 0 sysdig (29043) > sysdigevent event_type=1
    event_data=0
    5 10:54:50.463206317 0 sysdig (29043) > switch next=0 pgft_maj=0
    pgft_min=1790 vm_size=35748 vm_rss=7164 vm_swap=0
    6 10:54:50.464246835 0 (0) > switch next=7 pgft_maj=0 pgft_min=0
    vm_size=0 vm_rss=0 vm_swap=0
    7 10:54:50.464249707 2 (0) > switch next=8374 pgft_maj=0 pgft_min=0
    vm_size=0 vm_rss=0 vm_swap=0
    8 10:54:50.464255940 0 (7) > switch next=0 pgft_maj=0 pgft_min=0
    vm_size=0 vm_rss=0 vm_swap=0
    9 10:54:50.464264256 2 (8374) > switch next=0 pgft_maj=0 pgft_min=0
    vm_size=0 vm_rss=0 vm_swap=0
    10 10:54:50.464358113 2 (0) > switch next=854(mlnet) pgft_maj=0
    pgft_min=0 vm_size=0 vm_rss=0 vm_swap=0
    11 10:54:50.464370099 2 mlnet (854) < poll res=0 fds=
    12 10:54:50.464378193 2 mlnet (854) > poll fds= timeout=5
    13 10:54:50.464385400 2 mlnet (854) > switch next=0 pgft_maj=216
    pgft_min=3386 vm_size=162608 vm_rss=12196 vm_swap=2716
    14 10:54:50.464950541 0 (0) > switch next=1105(memcached) pgft_maj=0
    pgft_min=0 vm_size=0 vm_rss=0 vm_swap=0
    15 10:54:50.464954692 0 memcached (1105) < epoll_wait res=0
    16 10:54:50.464976007 0 memcached (1105) > epoll_wait maxevents=32
    17 10:54:50.464984030 0 memcached (1105) > switch next=0 pgft_maj=3
    pgft_min=247 vm_size=327412 vm_rss=1860 vm_swap=468
    18 10:54:50.465256687 2 (0) > switch next=2181(plugin-containe)
    pgft_maj=0 pgft_min=0 vm_size=0 vm_rss=0 vm_swap=0
    19 10:54:50.465261465 2 plugin-containe (2181) < poll res=0 fds=
    20 10:54:50.465297692 2 plugin-containe (2181) > getrlimit
    resource=3(RLIMIT_STACK)

通过 Sysdig 捕获的结果如上所示，每列的意思分别为：

1. 事件编号
2. 时间戳
3. CPU 编号
4. 进程名
5. 线程 ID
6. 事件方向，> 为进入事件，< 为退出事件
7. 事件类型，比如 open、read 等
8. 事件参数列表

**过滤捕获结果**

在默认情况下，Sysdig 捕获的信息非常多，要从中找到我们感兴趣的信息，这就需要类似 grep 的过滤功能。

按字段类别进行过滤：

    :::bash
    sysdig -r system.scap proc.name=sysdig

这条命令过滤出进程名为 sysdig 的系统事件，结果为：

    :::bash
    1 10:54:50.462463956 0 sysdig (29043) > sysdigevent event_type=1
    event_data=0
    2 10:54:50.462603110 0 sysdig (29043) > sysdigevent event_type=1
    event_data=0
    3 10:54:50.462729565 0 sysdig (29043) > sysdigevent event_type=1
    event_data=0
    4 10:54:50.462859521 0 sysdig (29043) > sysdigevent event_type=1
    event_data=0
    5 10:54:50.463206317 0 sysdig (29043) > switch next=0 pgft_maj=0
    pgft_min=1790 vm_size=35748 vm_rss=7164 vm_swap=0

Sysdig 提供包括 fd、process、evt、user、group、syslog 等字段类别，可通过 `sysdig -l` 查询。

除 = 外，Sysdig 的过滤表达式还支持 g=、<、<=、>、>= 及 contains 等比较操作符。

同时，也可以使用 and、or、not 等布尔操作符。例如：

    :::bash
    sysdig -r system.scap proc.name=sysdig and evt.type=switch

**Chisels**

在 Sysdig 中，chisels 是通过 Lua 编写的脚本，可以用来扩展 Sysdig 的过滤功能。

比如我们想看读写磁盘文件最频繁的进程，可以使用 topprocs_file 这个 chisels：

    :::bash
    sysdig -c topprocs_file

结果为：

    :::bash
    Bytes Process
    ------------------------------
    448.36KB mozStorage
    220.38KB perl
    1.69KB tmux
    1.62KB sh
    1.59KB Xorg
    1.30KB urxvtd

更多 chisels，可通过 `sysdig -cl` 了解。当然，如果你熟悉 Lua，那么也可以编写自己的 chisels。

[s]: http://www.sysdig.org


/*
 * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

package software.amazon.smithy.openapi.fromsmithy.security;

import software.amazon.smithy.model.traits.HttpBasicAuthTrait;
import software.amazon.smithy.model.traits.Trait;
import software.amazon.smithy.openapi.fromsmithy.Context;
import software.amazon.smithy.openapi.fromsmithy.SecuritySchemeConverter;
import software.amazon.smithy.openapi.model.SecurityScheme;
import software.amazon.smithy.utils.SmithyInternalApi;

/**
 * Applies Basic HTTP auth.
 */
@SmithyInternalApi
public final class HttpBasicConverter implements SecuritySchemeConverter<HttpBasicAuthTrait> {
    @Override
    public Class<HttpBasicAuthTrait> getAuthSchemeType() {
        return HttpBasicAuthTrait.class;
    }

    @Override
    public SecurityScheme createSecurityScheme(Context<? extends Trait> context, HttpBasicAuthTrait trait) {
        return SecurityScheme.builder()
                .type("http")
                .scheme("Basic")
                .description("HTTP Basic authentication")
                .build();
    }
}


/*
	This file is part of solidity.

	solidity is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	solidity is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0

#include <libsolidity/formal/Predicate.h>

#include <libsolidity/formal/SMTEncoder.h>

#include <libsolidity/ast/AST.h>

#include <boost/algorithm/string/join.hpp>
#include <utility>

using namespace std;
using namespace solidity;
using namespace solidity::smtutil;
using namespace solidity::frontend;
using namespace solidity::frontend::smt;

map<string, Predicate> Predicate::m_predicates;

Predicate const* Predicate::create(
	SortPointer _sort,
	string _name,
	PredicateType _type,
	EncodingContext& _context,
	ASTNode const* _node
)
{
	smt::SymbolicFunctionVariable predicate{_sort, move(_name), _context};
	string functorName = predicate.currentName();
	solAssert(!m_predicates.count(functorName), "");
	return &m_predicates.emplace(
		std::piecewise_construct,
		std::forward_as_tuple(functorName),
		std::forward_as_tuple(move(predicate), _type, _node)
	).first->second;
}

Predicate::Predicate(
	smt::SymbolicFunctionVariable&& _predicate,
	PredicateType _type,
	ASTNode const* _node
):
	m_predicate(move(_predicate)),
	m_type(_type),
	m_node(_node)
{
}

Predicate const* Predicate::predicate(string const& _name)
{
	return &m_predicates.at(_name);
}

void Predicate::reset()
{
	m_predicates.clear();
}

smtutil::Expression Predicate::operator()(vector<smtutil::Expression> const& _args) const
{
	return m_predicate(_args);
}

smtutil::Expression Predicate::functor() const
{
	return m_predicate.currentFunctionValue();
}

smtutil::Expression Predicate::functor(unsigned _idx) const
{
	return m_predicate.functionValueAtIndex(_idx);
}

void Predicate::newFunctor()
{
	m_predicate.increaseIndex();
}

ASTNode const* Predicate::programNode() const
{
	return m_node;
}

ContractDefinition const* Predicate::programContract() const
{
	if (auto const* contract = dynamic_cast<ContractDefinition const*>(m_node))
		if (!contract->constructor())
			return contract;

	return nullptr;
}

FunctionDefinition const* Predicate::programFunction() const
{
	if (auto const* contract = dynamic_cast<ContractDefinition const*>(m_node))
	{
		if (contract->constructor())
			return contract->constructor();
		return nullptr;
	}

	if (auto const* fun = dynamic_cast<FunctionDefinition const*>(m_node))
		return fun;

	return nullptr;
}

optional<vector<VariableDeclaration const*>> Predicate::stateVariables() const
{
	if (auto const* fun = programFunction())
		return SMTEncoder::stateVariablesIncludingInheritedAndPrivate(*fun);
	if (auto const* contract = programContract())
		return SMTEncoder::stateVariablesIncludingInheritedAndPrivate(*contract);

	auto const* node = m_node;
	while (auto const* scopable = dynamic_cast<Scopable const*>(node))
	{
		node = scopable->scope();
		if (auto const* fun = dynamic_cast<FunctionDefinition const*>(node))
			return SMTEncoder::stateVariablesIncludingInheritedAndPrivate(*fun);
	}

	return nullopt;
}

bool Predicate::isSummary() const
{
	return functor().name.rfind("summary", 0) == 0;
}

bool Predicate::isInterface() const
{
	return functor().name.rfind("interface", 0) == 0;
}

string Predicate::formatSummaryCall(vector<string> const& _args) const
{
	if (programContract())
		return "constructor()";

	solAssert(isSummary(), "");

	auto stateVars = stateVariables();
	solAssert(stateVars.has_value(), "");
	auto const* fun = programFunction();
	solAssert(fun, "");

	/// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postStateVars, postInputVars, outputVars).
	/// Here we are interested in preInputVars.
	vector<string>::const_iterator first = _args.begin() + static_cast<int>(stateVars->size()) + 1;
	vector<string>::const_iterator last = first + static_cast<int>(fun->parameters().size());
	solAssert(first >= _args.begin() && first <= _args.end(), "");
	solAssert(last >= _args.begin() && last <= _args.end(), "");
	vector<string> functionArgsCex(first, last);
	vector<string> functionArgs;

	auto const& params = fun->parameters();
	solAssert(params.size() == functionArgsCex.size(), "");
	for (unsigned i = 0; i < params.size(); ++i)
		if (params[i]->type()->isValueType())
			functionArgs.emplace_back(functionArgsCex[i]);
		else
			functionArgs.emplace_back(params[i]->name());

	string fName = fun->isConstructor() ? "constructor" :
		fun->isFallback() ? "fallback" :
		fun->isReceive() ? "receive" :
		fun->name();
	return fName + "(" + boost::algorithm::join(functionArgs, ", ") + ")";

}

vector<string> Predicate::summaryStateValues(vector<string> const& _args) const
{
	/// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postStateVars, postInputVars, outputVars).
	/// The signature of an implicit constructor summary predicate is: summary(error, postStateVars).
	/// Here we are interested in postStateVars.

	auto stateVars = stateVariables();
	solAssert(stateVars.has_value(), "");

	vector<string>::const_iterator stateFirst;
	vector<string>::const_iterator stateLast;
	if (auto const* function = programFunction())
	{
		stateFirst = _args.begin() + 1 + static_cast<int>(stateVars->size()) + static_cast<int>(function->parameters().size());
		stateLast = stateFirst + static_cast<int>(stateVars->size());
	}
	else if (programContract())
	{
		stateFirst = _args.begin() + 1;
		stateLast = stateFirst + static_cast<int>(stateVars->size());
	}
	else
		solAssert(false, "");

	solAssert(stateFirst >= _args.begin() && stateFirst <= _args.end(), "");
	solAssert(stateLast >= _args.begin() && stateLast <= _args.end(), "");

	vector<string> stateArgs(stateFirst, stateLast);
	solAssert(stateArgs.size() == stateVars->size(), "");
	return stateArgs;
}

vector<string> Predicate::summaryPostInputValues(vector<string> const& _args) const
{
	/// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postStateVars, postInputVars, outputVars).
	/// Here we are interested in postInputVars.
	auto const* function = programFunction();
	solAssert(function, "");

	auto stateVars = stateVariables();
	solAssert(stateVars.has_value(), "");

	auto const& inParams = function->parameters();

	vector<string>::const_iterator first = _args.begin() + 1 + static_cast<int>(stateVars->size()) * 2 + static_cast<int>(inParams.size());
	vector<string>::const_iterator last = first + static_cast<int>(inParams.size());

	solAssert(first >= _args.begin() && first <= _args.end(), "");
	solAssert(last >= _args.begin() && last <= _args.end(), "");

	vector<string> inValues(first, last);
	solAssert(inValues.size() == inParams.size(), "");
	return inValues;
}

vector<string> Predicate::summaryPostOutputValues(vector<string> const& _args) const
{
	/// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postStateVars, postInputVars, outputVars).
	/// Here we are interested in outputVars.
	auto const* function = programFunction();
	solAssert(function, "");

	auto stateVars = stateVariables();
	solAssert(stateVars.has_value(), "");

	auto const& inParams = function->parameters();

	vector<string>::const_iterator first = _args.begin() + 1 + static_cast<int>(stateVars->size()) * 2 + static_cast<int>(inParams.size()) * 2;

	solAssert(first >= _args.begin() && first <= _args.end(), "");

	vector<string> outValues(first, _args.end());
	solAssert(outValues.size() == function->returnParameters().size(), "");
	return outValues;
}


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.chenty.demoyolo">
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".Yolo"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>


<?php

namespace Grocy\Controllers;

use Grocy\Controllers\Users\User;
use Grocy\Services\ApiKeyService;
use Grocy\Services\ApplicationService;
use Grocy\Services\BatteriesService;
use Grocy\Services\CalendarService;
use Grocy\Services\ChoresService;
use Grocy\Services\DatabaseService;
use Grocy\Services\FilesService;
use Grocy\Services\LocalizationService;
use Grocy\Services\RecipesService;
use Grocy\Services\SessionService;
use Grocy\Services\StockService;
use Grocy\Services\TasksService;
use Grocy\Services\UserfieldsService;
use Grocy\Services\UsersService;

class BaseController
{
	protected $AppContainer;

	public function __construct(\DI\Container $container)
	{
		$this->AppContainer = $container;
		$this->View = $container->get('view');
	}

	protected function getApiKeyService()
	{
		return ApiKeyService::getInstance();
	}

	protected function getApplicationservice()
	{
		return ApplicationService::getInstance();
	}

	protected function getBatteriesService()
	{
		return BatteriesService::getInstance();
	}

	protected function getCalendarService()
	{
		return CalendarService::getInstance();
	}

	protected function getChoresService()
	{
		return ChoresService::getInstance();
	}

	protected function getDatabase()
	{
		return $this->getDatabaseService()->GetDbConnection();
	}

	protected function getDatabaseService()
	{
		return DatabaseService::getInstance();
	}

	protected function getFilesService()
	{
		return FilesService::getInstance();
	}

	protected function getLocalizationService()
	{
		return LocalizationService::getInstance(GROCY_LOCALE);
	}

	protected function getRecipesService()
	{
		return RecipesService::getInstance();
	}

	protected function getSessionService()
	{
		return SessionService::getInstance();
	}

	protected function getStockService()
	{
		return StockService::getInstance();
	}

	protected function getTasksService()
	{
		return TasksService::getInstance();
	}

	protected function getUserfieldsService()
	{
		return UserfieldsService::getInstance();
	}

	protected function getUsersService()
	{
		return UsersService::getInstance();
	}

	protected function render($response, $page, $data = [])
	{
		$container = $this->AppContainer;

		$versionInfo = $this->getApplicationService()->GetInstalledVersion();
		$this->View->set('version', $versionInfo->Version);
		$this->View->set('releaseDate', $versionInfo->ReleaseDate);

		$localizationService = $this->getLocalizationService();
		$this->View->set('__t', function (string $text, ...$placeholderValues) use ($localizationService) {
			return $localizationService->__t($text, $placeholderValues);
		});
		$this->View->set('__n', function ($number, $singularForm, $pluralForm) use ($localizationService) {
			return $localizationService->__n($number, $singularForm, $pluralForm);
		});
		$this->View->set('GettextPo', $localizationService->GetPoAsJsonString());

		$this->View->set('U', function ($relativePath, $isResource = false) use ($container) {
			return $container->get('UrlManager')->ConstructUrl($relativePath, $isResource);
		});

		$embedded = false;

		if (isset($_GET['embedded']))
		{
			$embedded = true;
		}

		$this->View->set('embedded', $embedded);

		$constants = get_defined_constants();

		foreach ($constants as $constant => $value)
		{
			if (substr($constant, 0, 19) !== 'GROCY_FEATURE_FLAG_')
			{
				unset($constants[$constant]);
			}
		}

		$this->View->set('featureFlags', $constants);

		if (GROCY_AUTHENTICATED)
		{
			$this->View->set('permissions', User::PermissionList());
		}

		return $this->View->render($response, $page, $data);
	}

	protected function renderPage($response, $page, $data = [])
	{
		$this->View->set('userentitiesForSidebar', $this->getDatabase()->userentities()->where('show_in_sidebar_menu = 1')->orderBy('name'));
		try
		{
			$usersService = $this->getUsersService();

			if (defined('GROCY_USER_ID'))
			{
				$this->View->set('userSettings', $usersService->GetUserSettings(GROCY_USER_ID));
			}
			else
			{
				$this->View->set('userSettings', null);
			}
		}
		catch (\Exception $ex)
		{
			// Happens when database is not initialised or migrated...
		}

		return $this->render($response, $page, $data);
	}
}


// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <gflags/gflags.h>
#include <glog/logging.h>
#include <paddle_inference_api.h>
#include <chrono>
#include <fstream>
#include <iostream>
#include <numeric>
#include <sstream>
#include <string>
#include <vector>

DEFINE_string(model_dir, "", "model directory");
DEFINE_string(data, "", "input data path");
DEFINE_int32(repeat, 1, "repeat");
DEFINE_bool(output_prediction, false, "Whether to output the prediction results.");
DEFINE_bool(use_gpu, false, "Whether to use GPU for prediction.");
DEFINE_int32(device, 0, "device.");

template <typename T>
void GetValueFromStream(std::stringstream *ss, T *t) {
  (*ss) >> (*t);
}

template <>
void GetValueFromStream<std::string>(std::stringstream *ss, std::string *t) {
  *t = ss->str();
}

// Split string to vector
template <typename T>
void Split(const std::string &line, char sep, std::vector<T> *v) {
  std::stringstream ss;
  T t;
  for (auto c : line) {
    if (c != sep) {
      ss << c;
    } else {
      GetValueFromStream<T>(&ss, &t);
      v->push_back(std::move(t));
      ss.str({});
      ss.clear();
    }
  }

  if (!ss.str().empty()) {
    GetValueFromStream<T>(&ss, &t);
    v->push_back(std::move(t));
    ss.str({});
    ss.clear();
  }
}

template <typename T>
constexpr paddle::PaddleDType GetPaddleDType();

template <>
constexpr paddle::PaddleDType GetPaddleDType<int64_t>() {
  return paddle::PaddleDType::INT64;
}

template <>
constexpr paddle::PaddleDType GetPaddleDType<float>() {
  return paddle::PaddleDType::FLOAT32;
}

// Parse tensor from string
template <typename T>
bool ParseTensor(const std::string &field, paddle::PaddleTensor *tensor) {
  std::vector<std::string> data;
  Split(field, ':', &data);
  if (data.size() < 2) return false;

  std::string shape_str = data[0];

  std::vector<int> shape;
  Split(shape_str, ' ', &shape);

  std::string mat_str = data[1];

  std::vector<T> mat;
  Split(mat_str, ' ', &mat);

  tensor->shape = shape;
  auto size =
      std::accumulate(shape.begin(), shape.end(), 1, std::multiplies<int>()) *
      sizeof(T);
  tensor->data.Resize(size);
  std::copy(mat.begin(), mat.end(), static_cast<T *>(tensor->data.data()));
  tensor->dtype = GetPaddleDType<T>();

  return true;
}

// Parse input tensors from string
bool ParseLine(const std::string &line,
               std::vector<paddle::PaddleTensor> *tensors) {
  std::vector<std::string> fields;
  Split(line, ';', &fields);

  tensors->clear();
  tensors->reserve(4);

  int i = 0;
  // src_ids
  paddle::PaddleTensor src_ids;
  ParseTensor<int64_t>(fields[i++], &src_ids);
  src_ids.name = "feed_0";
  tensors->push_back(src_ids);

  // sent_ids
  paddle::PaddleTensor sent_ids;
  ParseTensor<int64_t>(fields[i++], &sent_ids);
  sent_ids.name = "feed_1";
  tensors->push_back(sent_ids);

  return true;
}

// Print outputs to log
void PrintOutputs(const std::vector<paddle::PaddleTensor> &outputs) {
  //LOG(INFO) << "example_id\tcontradiction\tentailment\tneutral";
  for (size_t i = 0; i < outputs.front().data.length() / sizeof(float) / 3; i += 1) {
    std::cout << static_cast<float *>(outputs[0].data.data())[3 * i] << "\t"
         << static_cast<float *>(outputs[0].data.data())[3 * i + 1] << "\t"
         << static_cast<float *>(outputs[0].data.data())[3 * i + 2] << std::endl;
  }
}

bool LoadInputData(std::vector<std::vector<paddle::PaddleTensor>> *inputs) {
  if (FLAGS_data.empty()) {
    LOG(ERROR) << "please set input data path";
    return false;
  }

  std::ifstream fin(FLAGS_data);
  std::string line;

  int lineno = 0;
  while (std::getline(fin, line)) {
    std::vector<paddle::PaddleTensor> feed_data;
    if (!ParseLine(line, &feed_data)) {
      LOG(ERROR) << "Parse line[" << lineno << "] error!";
    } else {
      inputs->push_back(std::move(feed_data));
    }
  }

  return true;
}

// ernie inference demo
// Options:
//     --model_dir: ernie model file directory
//     --data: data path
//     --repeat: repeat num
//     --use_gpu: use gpu
int main(int argc, char *argv[]) {
  google::InitGoogleLogging(*argv);
  gflags::ParseCommandLineFlags(&argc, &argv, true);

  if (FLAGS_model_dir.empty()) {
    LOG(ERROR) << "please set model dir";
    return -1;
  }

  paddle::AnalysisConfig config;
  config.SetModel(FLAGS_model_dir);
  config.DisableGpu();
  config.SwitchIrOptim();
  config.EnableMKLDNN();
  config.SetCpuMathLibraryNumThreads(20);
  //config.EnableMemoryOptim();

  auto predictor = CreatePaddlePredictor(config);

  std::vector<std::vector<paddle::PaddleTensor>> inputs;
  if (!LoadInputData(&inputs)) {
    LOG(ERROR) << "load input data error!";
    return -1;
  }

  std::vector<paddle::PaddleTensor> fetch;
  int total_time{0};
  // auto predict_timer = []()
  int num_samples{0};
  int count{0};
  for (int i = 0; i < FLAGS_repeat; i++) {
    for (auto feed : inputs) {
      fetch.clear();
      auto start = std::chrono::system_clock::now();
      predictor->Run(feed, &fetch);
      if (FLAGS_output_prediction && i == 0) {
        PrintOutputs(fetch);
      }
      auto end = std::chrono::system_clock::now();
      count += 1;
      if (!fetch.empty()) {
        total_time +=
            std::chrono::duration_cast<std::chrono::milliseconds>(end - start)
                .count();
        //num_samples += fetch.front().data.length() / 2 / sizeof(float);
        num_samples += fetch.front().data.length() / (sizeof(float) * 2);
      }
    }
  }

  auto per_sample_ms =
      static_cast<float>(total_time) / num_samples;
  LOG(INFO) << "Run " << num_samples
            << " samples, average latency: " << per_sample_ms
            << "ms per sample.";
  LOG(INFO) << count;

  return 0;
}