text:
Pretty Ditch Weed – By PSY-23
Pretty Ditch Weed – By…
Never miss a copy of Weed World magazine, get it sent direct to your door or your smartphone. Subscribe here!
Cannabis newsUncategorised
After a brief hiatus, we recently heard from one of our good friends, Todd Moon aka Hempster Luna, and had the chance to catch up on his latest escapdes. Last time we spoke he was planning on heading off to a start a new venture, which sadly fell through, but, as the aying goes, you can't keep a good man down.
After travelling around and living in his car for a while, Todd headed over to Minnesota to see his cousin. His cousin is a forester and while they were out surveying the local area Todd found his attention drawn to an area which had been clear cut. He asked what they were doing with the newly cleared land and he couldn't believe his ears when the response given was that the land was going to be used for growing hemp.
He quickly realised that the owner of the land was, in fact, a total newbie who was planning to grow ginseng but saw the opportunity to make more from the land using hemp and jumped at the idea in spite of his lack of knowledge. Todd knew that he could prove to be invaluable for this new venture and jumped at the chance to get involved.
Although the land had been cleared in May, they had to wait until June to get the seeds signed off by the local authorities. Stringent regulations meant that they had to be very selective when it came to choosing their seed bank, but they went with Jupiter Seeds from Oregon due to their stellar reputation.
The freshly cleared land was far from perfect, but there was a good chance of success due to hemp's hardy nature and resilience. They planted close to 3000 seeds in total and once the grow was underway they found that around 2000 had taken successfully.
In total there were three clear phenotypes present in the batch of seeds they had sown and each had distinct characteristics which showed through. One looked like a mini-palm tree, the second was sticky and green while the third was purple and bushy. As time went on he noted that some of the plants now looked like little 4ft trees.
They had been late to plant as they had to wait for the all-clear from state officials before they could get to work, and this meant that they were tempted to follow suit of many other growers in the region and aim for a short growing period of 5 weeks in the hope of processing the plants for the extraction of isolate as plants grown in the Midwest tend to spike at 15% CBD after this period of time. Once the grow was taking shape, he made the call to let them grow to full-term and was amazed at the impact this had.
Fast forward a couple of months and they found themselves looking at a mountain of a task when it came to cropping and stripping the plants down. Whilst he had been off running some errands, Todd was amused-yet-terrified to discover that the guys at the farm started to try cropping with no idea of what they were doing. Before he could advise them of what to do, they had cut down 500 plants and had taken it upon themselves to begin removing the unwanted material.
Despite the fact that there was a decently sized team at the helm, they had only managed to hack their way through 10 plants in half a day. He had to take them aside and show them exactly what to do to speed things up and keep it accurate but also to try and in still a sense of moral after they found themselves feeling dejected and out of their depth.
Once they got into the swing everyone had an awesome time and Todd was educating them the whole time and encouraging everyone to sniff the individual plants so they could get a hit of the widely varied terpene profiles and see what they could pick out.
He felt that they managed to establish a real sense of teamwork and community and their excitement overcame that initial sense of adversity. After 8 full days of breaking them down and getting them hung they noted that some of the plants looked like purple Xmas trees when stripped properly as whole plants.
One week of trimming had led them to hit 200lbs easily which was a surprise for everyone. The field was quite poor as it had been stripped for the first time with no idea of what they were really prepping for and they also had some of the worst weather in a long time. For the last few weeks the plants were in fields that were ankle-deep in mud and water. Add to the mix the snow and horrendous temperatures and it was a miracle that they didn't run in to more issue, but there was no sign of mold or bud rot and no pest issues whatsoever. That's the magic of hemp!
Once they saw the sheer size and quality of their crop, Todd was excited about the level of success from this fledgling grow. Other growers reported that they were getting about $35 per lb for crops destined to be processed into isolate (which has to be sold as biomass according to state law) but he was aware that there was a distinct possibility of hitting up to $500 per lb for high concentration CBD flower. Being late to plant there was initially a distinct possibility they were going to go for isolate yet they held on to hit the end game point and now they find themselves sitting on more than 200lbs of flower with full certification from the State of Minnesota.
During the 38 days it took for official reports to come back to them, they had no option other than beginning the cropping and processing it although there was an ever-present danger that if they had crept over the .30% THC limit it would all have been written off and destroyed. Fortunately, it hit 16% CBD and stayed in limit for THC (0.194 through independent levels and 0.264 according to the official channels). As well as being state-certified and fully licensed, their plants delivered terpene levels that were truly excellent and made the plants a wonder to behold.
When Todd was showing it to one of his friends and explaining that this was indeed hemp and not its sister plant, the guy looked at him and said: "I'll tell you something – that's some pretty ditch weed!" Todd thought it was pretty cool to look at it in this way but he knew that it was a huge positive. He spoke about the idea of trying to 'reclaim the name' and went on to discuss how the hemp industry was shut down the back in the day when the government shifted focus from the prohibition of alcohol and turned their attention to 'weed' (leading to the formation of the DEA etc.). He informed me that Lincoln, Nebraska, was the Mecca of hemp and the idea of 'ditch weed' comes from the fact that it still grows wildly in the area.
During WWII the government reintroduced seeds in the area to grow hemp for the war effort but it was shut down after the war ended. Obviously, it's nigh-on impossible to completely remove plants like this from the wild and Todd noted that he had wanted to find the hemp genetics when he was in Colorado. Funny how things turn out.T
hey were all blown away by the standard of it and noted that there was no noticeable difference from traditional cannabis flower, so when it came to transporting trailer loads of flower across to Wisconsin they were unsurprisingly apprehensive (even though they were legal and legit). They didn't even get checked.
Throughout the grow, there were some concerns with the chances of being raided by authorities and the weather had been so cold that the stress on the plants was likely to have caused THC spikes. Many other people were getting caught out by this and it meant that many plants were destroyed to stay legal by other farmers. In some ways this gave them the chance to capitalize on the misfortune of others as small scale farm shops were selling eighths of CBD flower for $35 (with some places hitting $50 in Austin, Texas).
It's funny to do something legit which still feels illegal. The wave of legalization has had more than its fair share of hurdles but the expanding CBD industry seems to be turning things around and breaking down barriers bit by bit. To see fields of hemp looking as good as this is certainly something special and this latest venture for Hempster Luna shows that you can't keep a good plant down for too long either.
Written and Published By PSY-23 In Weed World Magazine Issue 144
Categories: Cannabis news, UncategorisedBy WW 28th July 2020 Leave a comment
Tags: articlecannabisditchhempherbmarijuanaPSY-23storystrainweedweed world magazineweedworldworld
Author: WW
PreviousPrevious post:TABS (Totally Accurate Battle Simulator) –XO/PCNextNext post:The Pheno Hunter – By Mendo Dope
Call for UK prisons to trial free cannabis to see if it cuts drug deaths
Here's Why Today Could Be a Huge Day for Cannabis
Recreational Rectal Use Of Cannabis (Or, What I Did For Science), By Sensi Seeds
Israel Importing Cannabis World First, By Ziv Genesove
The Philosopher's Stoned, By Psy-23
Medical cannabis for the treatment of chronic pain and other disorders: misconceptions and facts
Book Reviews(13)
Breeder(102)
Canna Clinical Trials(922)
Cannabis news(1397)
Cannabis Recipes(48)
Companies & events(7)
Competitions(2)
Concentrate(8)
Extracts(15)
Film / TV Reviews(12)
Game Reviews(20)
Grow Reports(67)
Hashish(34)
Instagrammer Of The Month(3)
Media Partner(4)
Medical News(563)
Music Reviews(11)
Product Reviews(4)
Professor Lee(58)
Seed Company(89)
Spirituality(6)
Step by Step Guides(35)
Strain Reviews(193)
Uncategorised(78)
Billing via Bank transfer
Pay directly using a bank transfer into our bank – use your Internet banking option or by go into a local branch of your bank. Please use your Order ID as the payment reference.
All bank transfer payments must be received by us before your order is shipped.
Bank Transfer details :-
WACCY LIMITED
Sort code - 09-01-28
Account number - 35946424
SWIFT/BIC: ABBYGB2LXXX
IBAN: GB21ABBY09012835946424
When paying for your order by posting cash,
Please include your order ID with your cash payment and we strongly recommend you send your cash via insured registered mail.
Send your payment to :-
The Barn Coedmawr Isaf
Llanio Road
Tregaron
SY25 6PX
Money /Postal order
We no longer accept accept International Money orders or UK postal orders.
Discreet packing & shipping
Your privacy is of the utmost importance to us..
All orders are sent out in plain packaging which is recycled where possible. There is absolutely no outside reference to Weed World whatsoever.
Products are sent by courier or Royal Mail.
Orders which are lost in transit will only be replaced if they were sent using one of the following postal options - Royal Mail Recorded Delivery, Royal Mail Special Delivery, Royal Mail Airsure or Courier delivery. Orders which are confiscated by customs do not fall under this replacement policy.
+441974 821518 rest of the world
1.1	We are committed to safeguarding the privacy of Weed World magazine visitors and customers.
1.2	This policy applies where we are acting as a data controller with respect to the personal data from weedworldmagazine.org visitors and service users; in other words, where we determine the purposes and means of the processing of that personal data.
1.3	By using our website and agreeing to this policy, you consent to our use of cookies in accordance with the terms of this policy.
2.1	In this Section 2 we have set out:
(a)	the general categories of personal data that we may process;
(b)	in the case of personal data that we did not obtain directly from you, the source and specific categories of that data;
(c)	the purposes for which we may process personal data; and
(d)	the legal bases of the processing.
2.2	We may process data about your use of our website weedworldmagazine.org and services ("usage data"). The usage data may include your IP address, geographical location, browser type and version, operating system, referral source, length of visit, page views and website navigation paths, as well as information about the timing, frequency and pattern of your service use. The source of the usage data is for our analytics tracking system. This usage data may be processed for the purposes of analysing the use of weedworldmagazine.org and services. The legal basis for this processing is our legitimate interests, namely for monitoring and improving our website and services.
2.3	We may process your account data ("account data"). The account data may include your name and email address. The account data may be processed for the purposes of operating our website, providing our product services, maintaining back-ups of our databases and communicating with you. The legal basis for this processing is for our legitimate interests, namely the proper administration of our website weedworldmagazine.org and business.
2.4	We may process information that you post for publication on our website weedworldmagazine.org ("publication data"). The publication data may be processed for the purposes of enabling such publication and administering our website and services. The legal basis for this processing is for our legitimate interests regarding news stories, namely the proper administration of our website and business.
2.5	We may process information relating to transactions, including purchases of goods and services, that you enter into with us and/or through our website weedworldmagazine.org ("transaction data"). The transaction data may include your contact details, your card details and the transaction details. The transaction data may be processed for the purpose of supplying the purchased goods and services and keeping proper records of those transactions. The legal basis for this processing is the performance of a temporary contract between you and us and for taking steps, at your request, to enter into such a contract and our legitimate interests, namely weedworldmagazine.org interest in the proper administration of our website and business.
2.6	We may process information that you provide to us for the purpose of subscribing to our email notifications and/or newsletters ("notification data"). The notification data may be processed for the purposes of sending you the relevant notifications and/or newsletters about weedworldmagazine.org. The legal basis for this processing is consent via you signing up to our newsletter weedworldmagazine.org.
2.7	In addition to the specific purposes for which we may process your personal data set out in this Section 2, we may also process any of your personal data where such processing is necessary for compliance with a legal obligation to which we are subject.
2.8	Please do not supply any other person's personal data to us, unless we prompt you to do so.
3.1	We may disclose your personal data to any member of our group of companies (this means our subsidiaries, our ultimate holding company and all its subsidiaries) insofar as reasonably necessary for the purposes, and on the legal bases, set out in this policy.
3.2	Financial transactions relating to our website weedworldmagazine.org and services are handled by our payment services providers. We will share transaction data with our payment services providers only to the extent necessary for the purposes of processing your payments, refunding such payments and dealing with complaints and queries relating to such payments and refunds.
3.3	In addition to the specific disclosures of personal data set out in this Section 3, we may disclose your personal data where such disclosure is necessary for compliance with a legal obligation to which we are subject. We may also disclose your personal data where such disclosure is necessary for the establishment, exercise or defence of legal claims, whether in court proceedings or in an administrative or out-of-court procedure.
4.	International transfers of your personal data
4.1	In this Section 4, we provide information about the circumstances in which your personal data may be transferred to countries outside the European Economic Area (EEA).
4.2	The hosting facilities for our website are situated in Australia. The European Commission has made an "adequacy decision" with respect to the data protection laws of this country. Appropriate safeguards, namely the use of standard data protection clauses adopted or approved by the European Commission, will protect transfers.
5. Retaining and deleting personal data
5.1 This Section 5 sets out our data retention policies and procedure, which are designed to help ensure that we comply with our legal obligations in relation to the retention and deletion of personal data.
5.2 Personal data that we process for any purpose or purposes shall not be kept for longer than is necessary for that purpose or those purposes.
5.3 We will retain your personal data as follows:
(a) personal data category will be retained for a minimum period of 4 years, and for a maximum period 6 years for financial purposes.
5.4 In some cases it is not possible for us to specify in advance the periods for which your personal data will be retained. In such cases, we will determine the period of retention based on the following criteria:
(a) the period of retention of personal data will be determined based on how long the customer wishes to purchase goods from ourselves and/or if they wish to be taken off our system.
5.5 Notwithstanding the other provisions of this Section 5, we may retain your personal data where such retention is necessary for compliance with a legal obligation to which we are subject, or in order to protect your vital interests or the vital interests of another natural person.
6.	Security of personal data
6.1	We will take appropriate technical and organisational precautions to secure your personal data and to prevent the loss, misuse or alteration of your personal data.
6.2	We will store all your personal data on secure servers, personal computers and in secure manual record-keeping systems.
6.3	The following personal data will be stored by us in encrypted form: your name, contact information and some cardholder data.
6.4	Data relating to your enquiries and financial transactions that is sent from your web browser to our web server, or from our web server to your web browser, will be protected using encryption technology.
6.5	You acknowledge that the transmission of unencrypted (or inadequately encrypted) data over the internet is inherently insecure, and we cannot guarantee the security of data sent over the internet.
6.6	You should ensure that your password is not susceptible to being guessed, whether by a person or a computer program. You are responsible for keeping the password you use for accessing our website confidential and we will not ask you for your password (except when you log in to our website).
7.1	We may update this policy from time to time by publishing a new version on our website weedworldmagazine.org.
8.1	In this Section 8, we have summarised the rights that you have under data protection law. Some of the rights are complex, and not all of the details have been included in our summaries. Accordingly, you should read the relevant laws and guidance from the regulatory authorities for a full explanation of these rights.
8.2	Your principal rights under data protection law are:
(a)	the right to access;
(b)	the right to rectification;
(c)	the right to erasure;
(d)	the right to restrict processing;
(e)	the right to object to processing;
(f)	the right to data portability;
(g)	the right to complain to a supervisory authority; and
(h)	the right to withdraw consent.
8.3	You have the right to confirmation as to whether or not we process your personal data and, where we do, access to the personal data, together with certain additional information. That additional information includes details of the purposes of the processing, the categories of personal data concerned and the recipients of the personal data. Providing the rights and freedoms of others are not affected, we will supply to you a copy of your personal data. The first copy will be provided free of charge, but additional copies may be subject to a reasonable fee. You can access your personal data by visiting weedworldmagazine.org when logged into our website.
8.4	You have the right to have any inaccurate personal data about you rectified and, taking into account the purposes of the processing, to have any incomplete personal data about you completed.
8.5	In some circumstances you have the right to the erasure of your personal data without undue delay. Those circumstances include: the personal data are no longer necessary in relation to the purposes for which they were collected or otherwise processed; you withdraw consent to consent-based processing; you object to the processing under certain rules of applicable data protection law; the processing is for direct marketing purposes; and the personal data have been unlawfully processed. However, there are exclusions of the right to erasure. The general exclusions include where processing is necessary: for exercising the right of freedom of expression and information; for compliance with a legal obligation; or for the establishment, exercise or defence of legal claims.
8.6	In some circumstances you have the right to restrict the processing of your personal data. Those circumstances are: you contest the accuracy of the personal data; processing is unlawful but you oppose erasure; we no longer need the personal data for the purposes of our processing, but you require personal data for the establishment, exercise or defence of legal claims; and you have objected to processing, pending the verification of that objection. Where processing has been restricted on this basis, we may continue to store your personal data. However, we will only otherwise process it: with your consent; for the establishment, exercise or defence of legal claims; for the protection of the rights of another natural or legal person; or for reasons of important public interest.
8.7	You have the right to object to our processing of your personal data on grounds relating to your particular situation, but only to the extent that the legal basis for the processing is that the processing is necessary for: the performance of a task carried out in the public interest or in the exercise of any official authority vested in us; or the purposes of the legitimate interests pursued by us or by a third party. If you make such an objection, we will cease to process the personal information unless we can demonstrate compelling legitimate grounds for the processing which override your interests, rights and freedoms, or the processing is for the establishment, exercise or defence of legal claims.
8.8	You have the right to object to our processing of your personal data for direct marketing purposes (including profiling for direct marketing purposes). If you make such an objection, we will cease to process your personal data for this purpose.
8.9	You have the right to object to our processing of your personal data for scientific or historical research purposes or statistical purposes on grounds relating to your particular situation, unless the processing is necessary for the performance of a task carried out for reasons of public interest.
8.10	To the extent that the legal basis for our processing of your personal data is:
(a)	consent; or
(b)	that the processing is necessary for the performance of a contract to which you are party or in order to take steps at your request prior to entering into a contract,
and such processing is carried out by automated means, you have the right to receive your personal data from us in a structured, commonly used and machine-readable format. However, this right does not apply where it would adversely affect the rights and freedoms of others.
8.11	If you consider that our processing of your personal information infringes data protection laws, you have a legal right to lodge a complaint with a supervisory authority responsible for data protection. You may do so in the EU member state of your habitual residence, your place of work or the place of the alleged infringement.
8.12	To the extent that the legal basis for our processing of your personal information is consent, you have the right to withdraw that consent at any time. Withdrawal will not affect the lawfulness of processing before the withdrawal.
8.13	You may exercise any of your rights in relation to your personal data by written notice to us.
9.	Third party websites
9.1	Our website includes hyperlinks to, and details of, third party websites.
9.2	We have no control over, and are not responsible for, the privacy policies and practices of third parties.
10.	Personal data of children
10.1	Our website weedworldmagazine.org and services are targeted at persons over the age of 18.
10.2	If we have reason to believe that we hold personal data of a person under that age in our databases, we will delete that personal data.
11.	Updating information
11.1	Please let us know if the personal information that we hold about you needs to be corrected or updated.
12.	Acting as a data processor
12.1	Insofar as we act as a data processor rather than a data controller, this policy shall not apply. Our legal obligations as a data processor are instead set out in the contract between us and the relevant data controller.
13.	About cookies
13.1	A cookie is a file containing an identifier (a string of letters and numbers) that is sent by a web server to a web browser and is stored by the browser. The identifier is then sent back to the server each time the browser requests a page from the server.
13.2	Cookies may be either "persistent" cookies or "session" cookies: a persistent cookie will be stored by a web browser and will remain valid until its set expiry date, unless deleted by the user before the expiry date; a session cookie, on the other hand, will expire at the end of the user session, when the web browser is closed.
13.3	Cookies do not typically contain any information that personally identifies a user, but personal information that we store about you may be linked to the information stored in and obtained from cookies.
14.	Cookies that we use
14.1	We use cookies for the following purposes:
(a)	authentication - we use cookies to identify you when you visit our website and as you navigate our website;
(b)	status - we use cookies to help us to determine if you are logged into our website;
(c)	shopping cart - we use cookies to maintain the state of your shopping cart as you navigate our website;
(d)	personalisation - we use cookies to store information about your preferences and to personalise our website for you;
(e)	security - we use cookies as an element of the security measures used to protect user accounts, including preventing fraudulent use of login credentials, and to protect our website and services generally;
(f)	cookie consent - we use cookies to store your preferences in relation to the use of cookies more generally.
15.1	Our service providers use cookies and those cookies may be stored on your computer when you visit our website.
15.2	We use Google Analytics to analyse the use of our website. Google Analytics gathers information about website use by means of cookies. The information gathered relating to our website is used to create reports about the use of our website. Google's privacy policy is available at: https://www.google.com/policies/privacy/.
16.1	Most browsers allow you to refuse to accept cookies and to delete cookies. The methods for doing so vary from browser to browser, and from version to version. You can however obtain up-to-date information about blocking and deleting cookies via these links:
(a)	https://support.google.com/chrome/answer/95647?hl=en (Chrome);
(b)	https://support.mozilla.org/en-US/kb/enable-and-disable-cookies-website-preferences (Firefox);
(c)	http://www.opera.com/help/tutorials/security/cookies/ (Opera);
(d)	https://support.microsoft.com/en-gb/help/17442/windows-internet-explorer-delete-manage-cookies (Internet Explorer);
(f)	https://privacy.microsoft.com/en-us/windows-10-microsoft-edge-and-privacy (Edge).
16.2	Blocking all cookies will have a negative impact upon the usability of many websites.
16.3	If you block cookies, you will not be able to use all the features on our website.
17.	Cookie preferences
17.1	You can manage your preferences relating to the use of cookies on our website by visiting: [URL]
18.1	This website is owned WW Enterprises Ltd and operated by weedworldmagazine.org
18.2	We are registered in England and Wales.
18.3	You can contact us:
(d)	by email, using the email address published on our website
19.	Data protection registration
19.1	We are registered as a data controller with the UK Information Commissioner's Office.
19.2	Our data protection registration number is ZA349621
20.	Data protection officer
20.1	Our data protection officer's contact details are: [email protected]
Weed World Info


Plantation Island Resort is a place for everyone, from romantic couples of all ages to families with children and groups.
Relax around one of the many pools at the resort or swim in the gorgeous lagoon, take part in the wide range of leisure activities and cultural excursions or sip a cocktail at one of the bar while you watch the sun setting in the Fiji Islands.
Plantation Island Resort is Fiji's largest offshore resort, nestled in a lush tropical paradise - one of the most beautiful & photographed islands in the South Pacific.
Our island has 7 kilometres of white sandy beaches & fringed by a necklace of coral reefs & crystal clear water.


text:
To mark the 95th anniversary of the birth of Aleksandr Solzhenitsyn
posted 12 Dec 2013, 11:03 by Rights in Russia [ updated 12 Dec 2013, 11:12 ]
Moscow city and Moscow region Ryazan region Stavropol region Human rights activists Human rights education
Ninety-five years have passed since the birth of the writer, historian, Soviet political prisoner and author of The Gulag Archipelago, Aleksandr Isayevich Solzhenitsyn.
Aleksandr Isayevich Solzhenitsyn was born on 11 December 1918 in Kislovodsk. In 1941 he graduated from the Department of Physics and Mathematics at Rostov University. Between 1939 and 1941 he also took correspondence courses with the Moscow Institute of History, Philosophy and Literature.
In winter 1941-42 he worked as a driver in the army. He completed artillery training, and between November 1942 and February 1945 he served at the front as commander of a reconnaissance artillery battery.
In 1945 he was arrested in connection with letters he had written to a school friend that had attracted the attention of the censors, mainly because of his irreverent comments about Stalin, and also on the grounds of his unpublished stories and essays.
He was sentenced by a Special Council of the NKVD to eight years in a labour camp.
He spent the first part of his sentence in mixed correctional labour camps, but was transferred in 1946 to a "sharashka" – part of a network of scientific research institutes run by the Ministry of State Security – to work with other prisoners as a mathematician.
In 1950 he was sent to special camps for political prisoners such as the Ekibastuz camp in Kazakhstan, where he worked as a labourer, bricklayer and foundry foreman.
He was not released after completing his prison term, but sentenced to "perpetual exile" in Kok-Terek in Kazakhstan.
During his exile he taught mathematics and physics in a village school and began to write in secret.
In April 1956 those sentenced under Article 58 were released from exile, and in June he travelled to Moscow.
From August 1956 he worked as a village teacher in the Vladimir Region and was rehabilitated.
In June 1957 he moved to Ryazan and worked as a physics and astronomy teacher in School No 2. He continued to write in secret.
To view photos of Solzhenitsyn collected by HRO.org, click HERE
In 1961 he entered into the literary mainstream. In November 1962 One Day in the Life of Ivan Denisovich, a story about the camps, was published in the magazine "Novy Mir" and became a sensation.
He continued to write about the time of the mass terror in the USSR and set up an entire network for the secret storage of his manuscripts and archives, as well as for the collection of information on the prisons, camps, exiles and deportations.
Some of his manuscripts were sent to the West. His work was published in samizdat and abroad.
In 1969 he was expelled from the Union of Writers. In October 1970 Solzhenitsyn was awarded the Nobel Prize in Literature. The KGB and party officials whipped up resentment against him.
In December 1973 the first volume of The Gulag Archipelago was published in Paris. Solzhenitsyn was subject to threats, open surveillance and a witch hunt in the Soviet press.
In February 1974 Solzhenitsyn was arrested and a criminal case against him was opened under Article 64 ("Betrayal of the Motherland"). On 13 February the KGB deported him to West Germany. The Supreme Soviet of the USSR stripped him of his Soviet citizenship.
Solzhenitsyn wrote a whole series of new works while in exile.
In 1998, during the perestroika period, publication of Solzhenitsyn's works began against in the USSR. "Novy Mir" in Moscow started to publish The Gulag Archipelago in August 1989.
In October 1990 Solzhenitsyn was made an honorary citizen of the city of Ryazan.
Aleksandr Solzhenitsyn in Ryazan. 1994. Photo by Evgeny Kashirin, Ryazan branch of "Memorial"
In September 1991 the Prosecutor General announced that the case opened in 1974 against Solzhenitsyn would be closed.
In 1994 Solzhenitsyn celebrated a triumphant return to Russia.
He was awarded several Russian orders and the State Prize.
In Russia he continued to work extensively on his cycle of novels about the Russian revolution.
On 3 August 2008 Aleksandr Solzhenitsyn died at his home near Moscow.
He was buried at the Donskoi Monastery cemetery in Moscow.
International Memorial Society: In memory of Aleksandr Isayevich Solzhenitsyn:
We have had the enviable but difficult fate of being the contemporaries of Aleksandr Solzhenitsyn. The great literary talent, expressed in a large variety of literary genres, and the unity of his biography and his creative work, make Solzhenitsyn one of the most outstanding figures of Russian and world culture in the 20th century.
Usually, the scale of a personality and the talent of the individual as an artist and a thinker are not immediately recognised. Sometimes it takes decades. Only now are we beginning to understand that we have lived at the same time and in the same country as Varlam Shalamov and Vasily Grossman.
However, with Alexander it was different: on that November morning in 1962, when literary Russia first opened the November issue of Novy mir, it became clear that Russian literature had entered a new phase.
In 1967, in a letter to the Congress of Soviet writers, a new Solzhenitsyn appeared before the reading public: a brilliant political essayist, an uncompromising fighter for civil liberties, and above all for freedom of thought and of speech.
Human rights activists saw Solzhenitsyn as one of their own; for several years in the country and the world he was seen as dissident, Number One.
But Solzhenitsyn was not just a dissident: he was able to combine within himself the political fighter against the regime and the individual, who dreamed of ending the two-century-long confrontation between government and society, of a Great Reconciliation of the Russian state with the Russian intelligentsia.
In the mid-1970 's, in his Letter to Soviet Leaders, in several essays published in a collection put together by him and his associates From Under the Rubble, and in articles written in exile, Solzhenitsyn emerges as an original and powerful political thinker.
His harsh criticism of modern democracy, of the secularization of Western society and of the other bases of modern European civilization gave him a firm reputation as someone who is anti-Western and even nationalist.
But Solzhenitsyn, like his predecessor Dostoevsky, does not fit into the framework of such definitions. His search for a "special way" for Russia was nothing other than a sincere attempt to connect what he considered were Russian national values with the Christian culture of Europe, with the continuation of the spiritual quest of Russian religious philosophers of the late 19th and early 20th centuries.
Neither we nor anyone else can claim to today that today, now, we can appreciate the value of the intellectual heritage of Solzhenitsyn's thought. The debates about Solzhenitsyn's historical views, his political philosophy and journalism will go on for many more decades.
And maybe these discussions, like the disputes around Dostoevsky and Tolstoy, will go on forever — at least, as long as the socio-cultural phenomenon of the Russian intelligentsia still lives on.
However, regardless of the attitude of Aleksandr Isayevich's contemporaries and descendants to his social and political views, the incredible energy, the fierce conviction and the literary brilliance with which Solzhenitsyn formulated and defended his views, in and of themselves, ensure that his writings stand out as a cultural achievement.
For us, for the international Memorial Society, Solzhenitsyn's Gulag Archipelago has tremendous significance.
In this, by the author's definition "an experiment in artistic investigation", he successfully combined two previously separate currents in the stream of memory about state terror: the direct personal testimony of witnesses and victims of the largest national catastrophe of the century, and attempts at critical reflection on both established and newly discovered historical data.
The main outcome of this work was not so much the new discoveries about the terror, but the establishment of an integrity of historical understanding.
In essence, the Gulag Archipelago is a colossal attempt to create a new national historical conscience, an alternative to the official version of Soviet history with its deceit, deliberate omissions and falsifications.
For many years to come, until the last years of perestroika, the samizdat text of the Gulag Archipelago would be one of those in most demand, and one of the texts for which readers were most persecuted .
It was seized in searches and those reading or possessing it were sacked from their jobs or expelled from university; those involved with its distribution and duplication were arrested and tried.
However, despite this, copies of foreign editions were secretly smuggled into the USSR, but here hundreds of copies of the book were produced by means of photography, copied by duplicating machines, and retyped on typewriters.
In the West, the Gulag Archipelago also had a massive influence as irrefutable evidence of the cost and outcomes of the Communist experiment.
The official abbreviation for "Main Administration for Camps" has become a metaphor, and this same word "Gulag" has entered all the languages of the world as one of the designations of the notion of a "humanitarian catastrophe of political origin with national or global scope."
The Gulag Archipelago opened a new phase in the understanding of the history of our country in the 20th century.
The need to work with the past for the sake of future became obvious to many people.
At first, there were dozens of such people, then there were hundreds and thousands of them. Aleksandr Solzhenitsyn's grand "experiment in artistic research" has been the starting point for that attempts at independent historical research in the 1970's, for the broad social movement that arose at the end of the 1980s, and for the work by Memorial that began in 1990 and continues to this day.
Now many will begin to talk of the "end of the era of Solzhenitsyn".
We categorically disagree. "The era of Solzhenitsyn," the era of the restoration of historical memory, does not end with his departure.
International Memorial Society
Translated by Graham Jones

redpajama_set_name: RedPajamaCommonCrawl


Take a few minutes to read these five tips to make sure you are ready for concealed carry.
Once, a long time ago, I sat in the pilot's seat on a commercial airliner. Despite the fact that I was in the hot seat and had all the equipment right there at my fingertips, I was far from a pilot. I was missing major pieces to the puzzle for flying a jet. After a brief moment I headed back to coach, the pilot took his seat in the cockpit and we were safely underway.
The realities of concealed carry are not far from my experience on the tarmac. Just because you have a gun and a permit does not mean you are ready for the demands of concealed carry. Whether you are brand new to carrying a defensive handgun for protection or you are an old hand at carrying concealed, up your game and take a few minutes to read these five tips to make sure you are ready for everyday carry.
Gun safety rules always need to be a top priority for everyone who carries a gun.
If you carry or plan on carrying concealed, you will be handling your gun a lot. You will need to load and unload your handgun, holster and unholster it, clean and repair it as well as store it appropriately. Everyone that carries concealed needs to perform these essential but rudimentary tasks on a regular basis.
In order to keep yourself and those around you safe while you are routinely handling your handgun, you need to make sure you are paying attention to safe gun handling. Safe gun handling requires that you know and follow basic gun safety rules, and it requires that you store your guns so they are not accessible to unauthorized users at any time.
Remember, you made the choice to have defensive firearms as part of your life, to increase your safety and the safety of those that you love. Making gun safety a top priority ensures that your defensive handgun does not become an additional risk to you or your family.
All of your hardware must work together when the cards are stacked against you.
Today it's not terribly difficult to find a reliable defensive handgun for concealed carry. Quality manufacturers and quality information make choices easier and easier. What can be difficult, however, is making sure that all of the pieces of your concealed-carry system work together.
Remember, it isn't just about the gun. Your defensive handgun needs to function reliably with your ammunition. It also needs to be carried in a holster/belt combination that is comfortable, concealable, safe and secure while allowing quick access for a draw when needed. Finally, it all needs to function with your lifestyle, wardrobe and body type.
These variables make the reliability of your gear as an entire system an unknown that needs to be quantified. The best way to accomplish this is to assemble your gear and enroll in a quality defensive training course and put yourself and your gear through a few days of intensive use on the range.
Take quality training courses before you need to defend yourself.
Making sure that you have quality defensive handgun training should be a given when it comes to preparing for concealed carry. Many people fail to realize that defensive handgun training is only the beginning of the training you need to truly be prepared.
Imagine you are attacked at contact distance. Do you have the physical skills you need to block the attack, gain control or strike so that drawing your gun doesn't result in a struggle for your handgun? Having practical unarmed skills might be the difference that allows you to arm yourself when you need to.
Are you ready to deal with injuries that you or other innocent people could suffer even if you prevail in a defensive encounter? Quality medical training is a necessity for anyone that is really interested in leading a safer life.
Comprehensive training in the legal aspects of self-defense and the use of lethal force is also a solid investment in your safety. Understanding how to avoid legal self-defense pitfalls before you need to use your concealed-carry handgun can help you to make the right choices ahead of time, and to take the proper actions before, during and immediately following a self-defense encounter.
Your cell phone is a quick way to get help from first responders.
Your training will help you to prepare in several ways for the aftermath of a potentially lethal violent encounter. One way is dealing with medical issues that routinely occur during violence, and the second is dealing with law enforcement and the investigation that is likely to occur following defensive gun use.
• What's the best way to contact 911? What needs to be communicated to the dispatchers to get the assistance you need as quickly as possible?
• What are ways that you can avoid being identified as a "bad guy" to other legally armed citizens and responding law enforcement officers?
• How will you make sure that the scene is both safe and secure so that you are not forced to continue using your firearm for personal defense? How will you ensure that key evidence remains available for gathering and investigation once the police arrive?
Wise people avoid the people, places and situations where violence is more likely.
During a recent trip, I spent several days in a state that has no provision for concealed-carry reciprocity. Of course, this means I was not carrying a concealed handgun. Would you be surprised to find out that this had no effect on my behavior? It's true.
I have committed to avoid violence regardless of the skills or the tools that I possess. I refuse to take risks just because I am armed, and you should not either. I have no desire to argue over a parking space, interfere in someone else's business or head to a "rough part of town" unless I absolutely have to.
Why? Violence is unpredictable, and even if you do everything "right" you can still lose. Work to avoid violence and you will be less likely to have to deal with the effects of violence.
Of course, there are times when violence comes looking for you. When that is the case it is time to act. Do so and act decisively. When there is another option, however, take it. Avoidance will minimize your exposure to violence and its nasty consequences. Avoidance needs to be a major component of your preparedness to carry a concealed handgun.
Of course, there is more to concealed carry than just these five tips. Preparing to deal with violence is best attacked through a constant process of refining your mindset, skills and tools. If you tend to these five areas, you will have built a solid foundation for concealed carry and increase your everyday readiness.


text:
No Joke! Villagers Turn Sea RED As They Slaughter 180 Whales On Faroe Islands [Photos]
Horrific images show a pod of whales being rounded up and slaughtered by villagers on the Faroe Islands.
Every summer, thousands of pilot and beaked whales are massacred in bays across the remote island as inhabitants prepare for the harsh winter months ahead.
Cambridge University student Alastair Ward, 22, was visiting the archipelago last month to celebrate his graduation when he and a friend stumbled across the bloodbath.
He said: 'We were walking round this bay when this family of locals ran over and said "you're lucky, there's a whale coming".
'We thought it would just be one being dragged in but more and more boats kept appearing on the horizon.
'I couldn't believe how many whales there were. They were driving them into the bay, prodding them with their oars.
'Once they got close enough, the whole town sprinted in and started hacking at them.
'Even the children were getting involved, pulling on the ropes and jumping on the carcasses.
'We were just sat there speechless and a bit upset but you couldn't really pull yourself away.'
The whole ordeal lasted around 30 minutes as more than 180 dead whales were cut apart in Sandavágur bay.
Children as young as five were seen lending a hand, using hooked ropes to pull in the highly intelligent marine mammals and then jumping on the carcasses.
Mr Ward said many of the stricken animals were left writhing around on the rocks for a long time before being put out of their misery.
He added: 'The squealing from the whales was horrible.
'They were putting hooks on ropes in their blowholes to pull them in and then hacking at them with knives.
'They didn't die in a very humane way.
'A lot of the locals were all saying how it's the same as farming but I couldn't rely agree with that.
'Children were jumping on top of them. They just have such a different attitude to us because they're brought up on it.'
The hunt, known locally as the grindadrap, provides the Faroese with enough whale meat to last the bitter winter months.
But it has been criticised by animal rights campaigners in the past, who say the ritual is cruel and unnecessary.
Photo: Three arrested for couple's murder in Ogun
Literary Critic, Ikhide Ikheloa criticizes Africans who still believe in the Bible


text:
/*
 * ng_ubt.c
 */

/*-
 * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin@yahoo.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Id: ng_ubt.c,v 1.16 2003/10/10 19:15:06 max Exp $
 * $FreeBSD$
 */

/*
 * NOTE: ng_ubt2 driver has a split personality. On one side it is
 * a USB device driver and on the other it is a Netgraph node. This
 * driver will *NOT* create traditional /dev/ enties, only Netgraph
 * node.
 *
 * NOTE ON LOCKS USED: ng_ubt2 drives uses 2 locks (mutexes)
 *
 * 1) sc_if_mtx - lock for device's interface #0 and #1. This lock is used
 *    by USB for any USB request going over device's interface #0 and #1,
 *    i.e. interrupt, control, bulk and isoc. transfers.
 *
 * 2) sc_ng_mtx - this lock is used to protect shared (between USB, Netgraph
 *    and Taskqueue) data, such as outgoing mbuf queues, task flags and hook
 *    pointer. This lock *SHOULD NOT* be grabbed for a long time. In fact,
 *    think of it as a spin lock.
 *
 * NOTE ON LOCKING STRATEGY: ng_ubt2 driver operates in 3 different contexts.
 *
 * 1) USB context. This is where all the USB related stuff happens. All
 *    callbacks run in this context. All callbacks are called (by USB) with
 *    appropriate interface lock held. It is (generally) allowed to grab
 *    any additional locks.
 *
 * 2) Netgraph context. This is where all the Netgraph related stuff happens.
 *    Since we mark node as WRITER, the Netgraph node will be "locked" (from
 *    Netgraph point of view). Any variable that is only modified from the
 *    Netgraph context does not require any additonal locking. It is generally
 *    *NOT* allowed to grab *ANY* additional locks. Whatever you do, *DO NOT*
 *    grab any lock in the Netgraph context that could cause de-scheduling of
 *    the Netgraph thread for significant amount of time. In fact, the only
 *    lock that is allowed in the Netgraph context is the sc_ng_mtx lock.
 *    Also make sure that any code that is called from the Netgraph context
 *    follows the rule above.
 *
 * 3) Taskqueue context. This is where ubt_task runs. Since we are generally
 *    NOT allowed to grab any lock that could cause de-scheduling in the
 *    Netgraph context, and, USB requires us to grab interface lock before
 *    doing things with transfers, it is safer to transition from the Netgraph
 *    context to the Taskqueue context before we can call into USB subsystem.
 *
 * So, to put everything together, the rules are as follows.
 *	It is OK to call from the USB context or the Taskqueue context into
 * the Netgraph context (i.e. call NG_SEND_xxx functions). In other words
 * it is allowed to call into the Netgraph context with locks held.
 *	Is it *NOT* OK to call from the Netgraph context into the USB context,
 * because USB requires us to grab interface locks, and, it is safer to
 * avoid it. So, to make things safer we set task flags to indicate which
 * actions we want to perform and schedule ubt_task which would run in the
 * Taskqueue context.
 *	Is is OK to call from the Taskqueue context into the USB context,
 * and, ubt_task does just that (i.e. grabs appropriate interface locks
 * before calling into USB).
 *	Access to the outgoing queues, task flags and hook pointer is
 * controlled by the sc_ng_mtx lock. It is an unavoidable evil. Again,
 * sc_ng_mtx should really be a spin lock (and it is very likely to an
 * equivalent of spin lock due to adaptive nature of FreeBSD mutexes).
 *	All USB callbacks accept softc pointer as a private data. USB ensures
 * that this pointer is valid.
 */

#include <sys/stdint.h>
#include <sys/stddef.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/condvar.h>
#include <sys/sysctl.h>
#include <sys/sx.h>
#include <sys/unistd.h>
#include <sys/callout.h>
#include <sys/malloc.h>
#include <sys/priv.h>

#include "usbdevs.h"
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>

#define	USB_DEBUG_VAR usb_debug
#include <dev/usb/usb_debug.h>
#include <dev/usb/usb_busdma.h>

#include <sys/mbuf.h>
#include <sys/taskqueue.h>

#include <netgraph/ng_message.h>
#include <netgraph/netgraph.h>
#include <netgraph/ng_parse.h>
#include <netgraph/bluetooth/include/ng_bluetooth.h>
#include <netgraph/bluetooth/include/ng_hci.h>
#include <netgraph/bluetooth/include/ng_ubt.h>
#include <netgraph/bluetooth/drivers/ubt/ng_ubt_var.h>

static int		ubt_modevent(module_t, int, void *);
static device_probe_t	ubt_probe;
static device_attach_t	ubt_attach;
static device_detach_t	ubt_detach;

static void		ubt_task_schedule(ubt_softc_p, int);
static task_fn_t	ubt_task;

#define	ubt_xfer_start(sc, i)	usbd_transfer_start((sc)->sc_xfer[(i)])

/* Netgraph methods */
static ng_constructor_t	ng_ubt_constructor;
static ng_shutdown_t	ng_ubt_shutdown;
static ng_newhook_t	ng_ubt_newhook;
static ng_connect_t	ng_ubt_connect;
static ng_disconnect_t	ng_ubt_disconnect;
static ng_rcvmsg_t	ng_ubt_rcvmsg;
static ng_rcvdata_t	ng_ubt_rcvdata;

/* Queue length */
static const struct ng_parse_struct_field	ng_ubt_node_qlen_type_fields[] =
{
	{ "queue", &ng_parse_int32_type, },
	{ "qlen",  &ng_parse_int32_type, },
	{ NULL, }
};
static const struct ng_parse_type		ng_ubt_node_qlen_type =
{
	&ng_parse_struct_type,
	&ng_ubt_node_qlen_type_fields
};

/* Stat info */
static const struct ng_parse_struct_field	ng_ubt_node_stat_type_fields[] =
{
	{ "pckts_recv", &ng_parse_uint32_type, },
	{ "bytes_recv", &ng_parse_uint32_type, },
	{ "pckts_sent", &ng_parse_uint32_type, },
	{ "bytes_sent", &ng_parse_uint32_type, },
	{ "oerrors",    &ng_parse_uint32_type, },
	{ "ierrors",    &ng_parse_uint32_type, },
	{ NULL, }
};
static const struct ng_parse_type		ng_ubt_node_stat_type =
{
	&ng_parse_struct_type,
	&ng_ubt_node_stat_type_fields
};

/* Netgraph node command list */
static const struct ng_cmdlist			ng_ubt_cmdlist[] =
{
	{
		NGM_UBT_COOKIE,
		NGM_UBT_NODE_SET_DEBUG,
		"set_debug",
		&ng_parse_uint16_type,
		NULL
	},
	{
		NGM_UBT_COOKIE,
		NGM_UBT_NODE_GET_DEBUG,
		"get_debug",
		NULL,
		&ng_parse_uint16_type
	},
	{
		NGM_UBT_COOKIE,
		NGM_UBT_NODE_SET_QLEN,
		"set_qlen",
		&ng_ubt_node_qlen_type,
		NULL
	},
	{
		NGM_UBT_COOKIE,
		NGM_UBT_NODE_GET_QLEN,
		"get_qlen",
		&ng_ubt_node_qlen_type,
		&ng_ubt_node_qlen_type
	},
	{
		NGM_UBT_COOKIE,
		NGM_UBT_NODE_GET_STAT,
		"get_stat",
		NULL,
		&ng_ubt_node_stat_type
	},
	{
		NGM_UBT_COOKIE,
		NGM_UBT_NODE_RESET_STAT,
		"reset_stat",
		NULL,
		NULL
	},
	{ 0, }
};

/* Netgraph node type */
static struct ng_type	typestruct =
{
	.version = 	NG_ABI_VERSION,
	.name =		NG_UBT_NODE_TYPE,
	.constructor =	ng_ubt_constructor,
	.rcvmsg =	ng_ubt_rcvmsg,
	.shutdown =	ng_ubt_shutdown,
	.newhook =	ng_ubt_newhook,
	.connect =	ng_ubt_connect,
	.rcvdata =	ng_ubt_rcvdata,
	.disconnect =	ng_ubt_disconnect,
	.cmdlist =	ng_ubt_cmdlist
};

/****************************************************************************
 ****************************************************************************
 **                              USB specific
 ****************************************************************************
 ****************************************************************************/

/* USB methods */
static usb_callback_t	ubt_ctrl_write_callback;
static usb_callback_t	ubt_intr_read_callback;
static usb_callback_t	ubt_bulk_read_callback;
static usb_callback_t	ubt_bulk_write_callback;
static usb_callback_t	ubt_isoc_read_callback;
static usb_callback_t	ubt_isoc_write_callback;

static int		ubt_fwd_mbuf_up(ubt_softc_p, struct mbuf **);
static int		ubt_isoc_read_one_frame(struct usb_xfer *, int);

/*
 * USB config
 *
 * The following desribes usb transfers that could be submitted on USB device.
 *
 * Interface 0 on the USB device must present the following endpoints
 *	1) Interrupt endpoint to receive HCI events
 *	2) Bulk IN endpoint to receive ACL data
 *	3) Bulk OUT endpoint to send ACL data
 *
 * Interface 1 on the USB device must present the following endpoints
 *	1) Isochronous IN endpoint to receive SCO data
 *	2) Isochronous OUT endpoint to send SCO data
 */

static const struct usb_config		ubt_config[UBT_N_TRANSFER] =
{
	/*
	 * Interface #0
 	 */

	/* Outgoing bulk transfer - ACL packets */
	[UBT_IF_0_BULK_DT_WR] = {
		.type =		UE_BULK,
		.endpoint =	UE_ADDR_ANY,
		.direction =	UE_DIR_OUT,
		.if_index = 	0,
		.bufsize =	UBT_BULK_WRITE_BUFFER_SIZE,
		.flags =	{ .pipe_bof = 1, .force_short_xfer = 1, },
		.callback =	&ubt_bulk_write_callback,
	},
	/* Incoming bulk transfer - ACL packets */
	[UBT_IF_0_BULK_DT_RD] = {
		.type =		UE_BULK,
		.endpoint =	UE_ADDR_ANY,
		.direction =	UE_DIR_IN,
		.if_index = 	0,
		.bufsize =	UBT_BULK_READ_BUFFER_SIZE,
		.flags =	{ .pipe_bof = 1, .short_xfer_ok = 1, },
		.callback =	&ubt_bulk_read_callback,
	},
	/* Incoming interrupt transfer - HCI events */
	[UBT_IF_0_INTR_DT_RD] = {
		.type =		UE_INTERRUPT,
		.endpoint =	UE_ADDR_ANY,
		.direction =	UE_DIR_IN,
		.if_index = 	0,
		.flags =	{ .pipe_bof = 1, .short_xfer_ok = 1, },
		.bufsize =	UBT_INTR_BUFFER_SIZE,
		.callback =	&ubt_intr_read_callback,
	},
	/* Outgoing control transfer - HCI commands */
	[UBT_IF_0_CTRL_DT_WR] = {
		.type =		UE_CONTROL,
		.endpoint =	0x00,	/* control pipe */
		.direction =	UE_DIR_ANY,
		.if_index = 	0,
		.bufsize =	UBT_CTRL_BUFFER_SIZE,
		.callback =	&ubt_ctrl_write_callback,
		.timeout =	5000,	/* 5 seconds */
	},

	/*
	 * Interface #1
 	 */

	/* Incoming isochronous transfer #1 - SCO packets */
	[UBT_IF_1_ISOC_DT_RD1] = {
		.type =		UE_ISOCHRONOUS,
		.endpoint =	UE_ADDR_ANY,
		.direction =	UE_DIR_IN,
		.if_index = 	1,
		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
		.frames =	UBT_ISOC_NFRAMES,
		.flags =	{ .short_xfer_ok = 1, },
		.callback =	&ubt_isoc_read_callback,
	},
	/* Incoming isochronous transfer #2 - SCO packets */
	[UBT_IF_1_ISOC_DT_RD2] = {
		.type =		UE_ISOCHRONOUS,
		.endpoint =	UE_ADDR_ANY,
		.direction =	UE_DIR_IN,
		.if_index = 	1,
		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
		.frames =	UBT_ISOC_NFRAMES,
		.flags =	{ .short_xfer_ok = 1, },
		.callback =	&ubt_isoc_read_callback,
	},
	/* Outgoing isochronous transfer #1 - SCO packets */
	[UBT_IF_1_ISOC_DT_WR1] = {
		.type =		UE_ISOCHRONOUS,
		.endpoint =	UE_ADDR_ANY,
		.direction =	UE_DIR_OUT,
		.if_index = 	1,
		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
		.frames =	UBT_ISOC_NFRAMES,
		.flags =	{ .short_xfer_ok = 1, },
		.callback =	&ubt_isoc_write_callback,
	},
	/* Outgoing isochronous transfer #2 - SCO packets */
	[UBT_IF_1_ISOC_DT_WR2] = {
		.type =		UE_ISOCHRONOUS,
		.endpoint =	UE_ADDR_ANY,
		.direction =	UE_DIR_OUT,
		.if_index = 	1,
		.bufsize =	0,	/* use "wMaxPacketSize * frames" */
		.frames =	UBT_ISOC_NFRAMES,
		.flags =	{ .short_xfer_ok = 1, },
		.callback =	&ubt_isoc_write_callback,
	},
};

/*
 * If for some reason device should not be attached then put
 * VendorID/ProductID pair into the list below. The format is
 * as follows:
 *
 *	{ USB_VPI(VENDOR_ID, PRODUCT_ID, 0) },
 *
 * where VENDOR_ID and PRODUCT_ID are hex numbers.
 */

static const STRUCT_USB_HOST_ID ubt_ignore_devs[] =
{
	/* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */
	{ USB_VPI(USB_VENDOR_AVM, 0x2200, 0) },

	/* Atheros 3011 with sflash firmware */
	{ USB_VPI(0x0cf3, 0x3002, 0) },
	{ USB_VPI(0x0cf3, 0xe019, 0) },
	{ USB_VPI(0x13d3, 0x3304, 0) },
	{ USB_VPI(0x0930, 0x0215, 0) },
	{ USB_VPI(0x0489, 0xe03d, 0) },
	{ USB_VPI(0x0489, 0xe027, 0) },

	/* Atheros AR9285 Malbec with sflash firmware */
	{ USB_VPI(0x03f0, 0x311d, 0) },

	/* Atheros 3012 with sflash firmware */
	{ USB_VPI(0x0cf3, 0x3004, 0) },
	{ USB_VPI(0x0cf3, 0x311d, 0) },
	{ USB_VPI(0x13d3, 0x3375, 0) },
	{ USB_VPI(0x04ca, 0x3005, 0) },
	{ USB_VPI(0x04ca, 0x3006, 0) },
	{ USB_VPI(0x04ca, 0x3008, 0) },
	{ USB_VPI(0x13d3, 0x3362, 0) },
	{ USB_VPI(0x0cf3, 0xe004, 0) },
	{ USB_VPI(0x0930, 0x0219, 0) },
	{ USB_VPI(0x0489, 0xe057, 0) },
	{ USB_VPI(0x13d3, 0x3393, 0) },
	{ USB_VPI(0x0489, 0xe04e, 0) },
	{ USB_VPI(0x0489, 0xe056, 0) },

	/* Atheros AR5BBU12 with sflash firmware */
	{ USB_VPI(0x0489, 0xe02c, 0) },

	/* Atheros AR5BBU12 with sflash firmware */
	{ USB_VPI(0x0489, 0xe03c, 0) },
	{ USB_VPI(0x0489, 0xe036, 0) },
};

/* List of supported bluetooth devices */
static const STRUCT_USB_HOST_ID ubt_devs[] =
{
	/* Generic Bluetooth class devices */
	{ USB_IFACE_CLASS(UDCLASS_WIRELESS),
	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },

	/* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */
	{ USB_VPI(USB_VENDOR_AVM, 0x3800, 0) },

	/* Broadcom USB dongles, mostly BCM20702 and BCM20702A0 */
	{ USB_VENDOR(USB_VENDOR_BROADCOM),
	  USB_IFACE_CLASS(UICLASS_VENDOR),
	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },

	/* Apple-specific (Broadcom) devices */
	{ USB_VENDOR(USB_VENDOR_APPLE),
	  USB_IFACE_CLASS(UICLASS_VENDOR),
	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },

	/* Foxconn - Hon Hai */
	{ USB_VENDOR(USB_VENDOR_FOXCONN),
	  USB_IFACE_CLASS(UICLASS_VENDOR),
	  USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
	  USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },

	/* MediaTek MT76x0E */
	{ USB_VPI(USB_VENDOR_MEDIATEK, 0x763f, 0) },

	/* Broadcom SoftSailing reporting vendor specific */
	{ USB_VPI(USB_VENDOR_BROADCOM, 0x21e1, 0) },

	/* Apple MacBookPro 7,1 */
	{ USB_VPI(USB_VENDOR_APPLE, 0x8213, 0) },

	/* Apple iMac11,1 */
	{ USB_VPI(USB_VENDOR_APPLE, 0x8215, 0) },

	/* Apple MacBookPro6,2 */
	{ USB_VPI(USB_VENDOR_APPLE, 0x8218, 0) },

	/* Apple MacBookAir3,1, MacBookAir3,2 */
	{ USB_VPI(USB_VENDOR_APPLE, 0x821b, 0) },

	/* Apple MacBookAir4,1 */
	{ USB_VPI(USB_VENDOR_APPLE, 0x821f, 0) },

	/* MacBookAir6,1 */
	{ USB_VPI(USB_VENDOR_APPLE, 0x828f, 0) },

	/* Apple MacBookPro8,2 */
	{ USB_VPI(USB_VENDOR_APPLE, 0x821a, 0) },

	/* Apple MacMini5,1 */
	{ USB_VPI(USB_VENDOR_APPLE, 0x8281, 0) },

	/* Bluetooth Ultraport Module from IBM */
	{ USB_VPI(USB_VENDOR_TDK, 0x030a, 0) },

	/* ALPS Modules with non-standard ID */
	{ USB_VPI(USB_VENDOR_ALPS, 0x3001, 0) },
	{ USB_VPI(USB_VENDOR_ALPS, 0x3002, 0) },

	{ USB_VPI(USB_VENDOR_ERICSSON2, 0x1002, 0) },

	/* Canyon CN-BTU1 with HID interfaces */
	{ USB_VPI(USB_VENDOR_CANYON, 0x0000, 0) },

	/* Broadcom BCM20702A0 */
	{ USB_VPI(USB_VENDOR_ASUS, 0x17b5, 0) },
	{ USB_VPI(USB_VENDOR_ASUS, 0x17cb, 0) },
	{ USB_VPI(USB_VENDOR_LITEON, 0x2003, 0) },
	{ USB_VPI(USB_VENDOR_FOXCONN, 0xe042, 0) },
	{ USB_VPI(USB_VENDOR_DELL, 0x8197, 0) },
};

/*
 * Probe for a USB Bluetooth device.
 * USB context.
 */

static int
ubt_probe(device_t dev)
{
	struct usb_attach_arg	*uaa = device_get_ivars(dev);
	int error;

	if (uaa->usb_mode != USB_MODE_HOST)
		return (ENXIO);

	if (uaa->info.bIfaceIndex != 0)
		return (ENXIO);

	if (usbd_lookup_id_by_uaa(ubt_ignore_devs,
			sizeof(ubt_ignore_devs), uaa) == 0)
		return (ENXIO);

	error = usbd_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa);
	if (error == 0)
		return (BUS_PROBE_GENERIC);
	return (error);
} /* ubt_probe */

/*
 * Attach the device.
 * USB context.
 */

static int
ubt_attach(device_t dev)
{
	struct usb_attach_arg		*uaa = device_get_ivars(dev);
	struct ubt_softc		*sc = device_get_softc(dev);
	struct usb_endpoint_descriptor	*ed;
	struct usb_interface_descriptor *id;
	struct usb_interface		*iface;
	uint16_t			wMaxPacketSize;
	uint8_t				alt_index, i, j;
	uint8_t				iface_index[2] = { 0, 1 };

	device_set_usb_desc(dev);

	sc->sc_dev = dev;
	sc->sc_debug = NG_UBT_WARN_LEVEL;

	/*
	 * Create Netgraph node
	 */

	if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
		UBT_ALERT(sc, "could not create Netgraph node\n");
		return (ENXIO);
	}

	/* Name Netgraph node */
	if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) {
		UBT_ALERT(sc, "could not name Netgraph node\n");
		NG_NODE_UNREF(sc->sc_node);
		return (ENXIO);
	}
	NG_NODE_SET_PRIVATE(sc->sc_node, sc);
	NG_NODE_FORCE_WRITER(sc->sc_node);

	/*
	 * Initialize device softc structure
	 */

	/* initialize locks */
	mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF);
	mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE);

	/* initialize packet queues */
	NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN);
	NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN);
	NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN);

	/* initialize glue task */
	TASK_INIT(&sc->sc_task, 0, ubt_task, sc);

	/*
	 * Configure Bluetooth USB device. Discover all required USB
	 * interfaces and endpoints.
	 *
	 * USB device must present two interfaces:
	 * 1) Interface 0 that has 3 endpoints
	 *	1) Interrupt endpoint to receive HCI events
	 *	2) Bulk IN endpoint to receive ACL data
	 *	3) Bulk OUT endpoint to send ACL data
	 *
	 * 2) Interface 1 then has 2 endpoints
	 *	1) Isochronous IN endpoint to receive SCO data
 	 *	2) Isochronous OUT endpoint to send SCO data
	 *
	 * Interface 1 (with isochronous endpoints) has several alternate
	 * configurations with different packet size.
	 */

	/*
	 * For interface #1 search alternate settings, and find
	 * the descriptor with the largest wMaxPacketSize
	 */

	wMaxPacketSize = 0;
	alt_index = 0;
	i = 0;
	j = 0;
	ed = NULL;

	/*
	 * Search through all the descriptors looking for the largest
	 * packet size:
	 */
	while ((ed = (struct usb_endpoint_descriptor *)usb_desc_foreach(
	    usbd_get_config_descriptor(uaa->device),
	    (struct usb_descriptor *)ed))) {

		if ((ed->bDescriptorType == UDESC_INTERFACE) &&
		    (ed->bLength >= sizeof(*id))) {
			id = (struct usb_interface_descriptor *)ed;
			i = id->bInterfaceNumber;
			j = id->bAlternateSetting;
		}

		if ((ed->bDescriptorType == UDESC_ENDPOINT) &&
		    (ed->bLength >= sizeof(*ed)) &&
		    (i == 1)) {
			uint16_t temp;

			temp = UGETW(ed->wMaxPacketSize);
			if (temp > wMaxPacketSize) {
				wMaxPacketSize = temp;
				alt_index = j;
			}
		}
	}

	/* Set alt configuration on interface #1 only if we found it */
	if (wMaxPacketSize > 0 &&
	    usbd_set_alt_interface_index(uaa->device, 1, alt_index)) {
		UBT_ALERT(sc, "could not set alternate setting %d " \
			"for interface 1!\n", alt_index);
		goto detach;
	}

	/* Setup transfers for both interfaces */
	if (usbd_transfer_setup(uaa->device, iface_index, sc->sc_xfer,
			ubt_config, UBT_N_TRANSFER, sc, &sc->sc_if_mtx)) {
		UBT_ALERT(sc, "could not allocate transfers\n");
		goto detach;
	}

	/* Claim all interfaces belonging to the Bluetooth part */
	for (i = 1;; i++) {
		iface = usbd_get_iface(uaa->device, i);
		if (iface == NULL)
			break;
		id = usbd_get_interface_descriptor(iface);

		if ((id != NULL) &&
		    (id->bInterfaceClass == UICLASS_WIRELESS) &&
		    (id->bInterfaceSubClass == UISUBCLASS_RF) &&
		    (id->bInterfaceProtocol == UIPROTO_BLUETOOTH)) {
			usbd_set_parent_iface(uaa->device, i,
			    uaa->info.bIfaceIndex);
		}
	}
	return (0); /* success */

detach:
	ubt_detach(dev);

	return (ENXIO);
} /* ubt_attach */

/*
 * Detach the device.
 * USB context.
 */

int
ubt_detach(device_t dev)
{
	struct ubt_softc	*sc = device_get_softc(dev);
	node_p			node = sc->sc_node;

	/* Destroy Netgraph node */
	if (node != NULL) {
		sc->sc_node = NULL;
		NG_NODE_REALLY_DIE(node);
		ng_rmnode_self(node);
	}

	/* Make sure ubt_task in gone */
	taskqueue_drain(taskqueue_swi, &sc->sc_task);

	/* Free USB transfers, if any */
	usbd_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER);

	/* Destroy queues */
	UBT_NG_LOCK(sc);
	NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq);
	NG_BT_MBUFQ_DESTROY(&sc->sc_aclq);
	NG_BT_MBUFQ_DESTROY(&sc->sc_scoq);
	UBT_NG_UNLOCK(sc);

	mtx_destroy(&sc->sc_if_mtx);
	mtx_destroy(&sc->sc_ng_mtx);

	return (0);
} /* ubt_detach */

/*
 * Called when outgoing control request (HCI command) has completed, i.e.
 * HCI command was sent to the device.
 * USB context.
 */

static void
ubt_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error)
{
	struct ubt_softc		*sc = usbd_xfer_softc(xfer);
	struct usb_device_request	req;
	struct mbuf			*m;
	struct usb_page_cache		*pc;
	int				actlen;

	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);

	switch (USB_GET_STATE(xfer)) {
	case USB_ST_TRANSFERRED:
		UBT_INFO(sc, "sent %d bytes to control pipe\n", actlen);
		UBT_STAT_BYTES_SENT(sc, actlen);
		UBT_STAT_PCKTS_SENT(sc);
		/* FALLTHROUGH */

	case USB_ST_SETUP:
send_next:
		/* Get next command mbuf, if any */
		UBT_NG_LOCK(sc);
		NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
		UBT_NG_UNLOCK(sc);

		if (m == NULL) {
			UBT_INFO(sc, "HCI command queue is empty\n");
			break;	/* transfer complete */
		}

		/* Initialize a USB control request and then schedule it */
		bzero(&req, sizeof(req));
		req.bmRequestType = UBT_HCI_REQUEST;
		USETW(req.wLength, m->m_pkthdr.len);

		UBT_INFO(sc, "Sending control request, " \
			"bmRequestType=0x%02x, wLength=%d\n",
			req.bmRequestType, UGETW(req.wLength));

		pc = usbd_xfer_get_frame(xfer, 0);
		usbd_copy_in(pc, 0, &req, sizeof(req));
		pc = usbd_xfer_get_frame(xfer, 1);
		usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);

		usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
		usbd_xfer_set_frame_len(xfer, 1, m->m_pkthdr.len);
		usbd_xfer_set_frames(xfer, 2);

		NG_FREE_M(m);

		usbd_transfer_submit(xfer);
		break;

	default: /* Error */
		if (error != USB_ERR_CANCELLED) {
			UBT_WARN(sc, "control transfer failed: %s\n",
				usbd_errstr(error));

			UBT_STAT_OERROR(sc);
			goto send_next;
		}

		/* transfer cancelled */
		break;
	}
} /* ubt_ctrl_write_callback */

/*
 * Called when incoming interrupt transfer (HCI event) has completed, i.e.
 * HCI event was received from the device.
 * USB context.
 */

static void
ubt_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)
{
	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
	struct mbuf		*m;
	ng_hci_event_pkt_t	*hdr;
	struct usb_page_cache	*pc;
	int			actlen;

	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);

	m = NULL;

	switch (USB_GET_STATE(xfer)) {
	case USB_ST_TRANSFERRED:
		/* Allocate a new mbuf */
		MGETHDR(m, M_NOWAIT, MT_DATA);
		if (m == NULL) {
			UBT_STAT_IERROR(sc);
			goto submit_next;
		}

		MCLGET(m, M_NOWAIT);
		if (!(m->m_flags & M_EXT)) {
			UBT_STAT_IERROR(sc);
			goto submit_next;
		}

		/* Add HCI packet type */
		*mtod(m, uint8_t *)= NG_HCI_EVENT_PKT;
		m->m_pkthdr.len = m->m_len = 1;

		if (actlen > MCLBYTES - 1)
			actlen = MCLBYTES - 1;

		pc = usbd_xfer_get_frame(xfer, 0);
		usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
		m->m_pkthdr.len += actlen;
		m->m_len += actlen;

		UBT_INFO(sc, "got %d bytes from interrupt pipe\n",
			actlen);

		/* Validate packet and send it up the stack */
		if (m->m_pkthdr.len < (int)sizeof(*hdr)) {
			UBT_INFO(sc, "HCI event packet is too short\n");

			UBT_STAT_IERROR(sc);
			goto submit_next;
		}

		hdr = mtod(m, ng_hci_event_pkt_t *);
		if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) {
			UBT_ERR(sc, "Invalid HCI event packet size, " \
				"length=%d, pktlen=%d\n",
				hdr->length, m->m_pkthdr.len);

			UBT_STAT_IERROR(sc);
			goto submit_next;
		}

		UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \
			"length=%d\n", m->m_pkthdr.len, hdr->length);

		UBT_STAT_PCKTS_RECV(sc);
		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);

		ubt_fwd_mbuf_up(sc, &m);
		/* m == NULL at this point */
		/* FALLTHROUGH */

	case USB_ST_SETUP:
submit_next:
		NG_FREE_M(m); /* checks for m != NULL */

		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
		usbd_transfer_submit(xfer);
		break;

	default: /* Error */
		if (error != USB_ERR_CANCELLED) {
			UBT_WARN(sc, "interrupt transfer failed: %s\n",
				usbd_errstr(error));

			/* Try to clear stall first */
			usbd_xfer_set_stall(xfer);
			goto submit_next;
		}
			/* transfer cancelled */
		break;
	}
} /* ubt_intr_read_callback */

/*
 * Called when incoming bulk transfer (ACL packet) has completed, i.e.
 * ACL packet was received from the device.
 * USB context.
 */

static void
ubt_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
{
	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
	struct mbuf		*m;
	ng_hci_acldata_pkt_t	*hdr;
	struct usb_page_cache	*pc;
	int len;
	int actlen;

	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);

	m = NULL;

	switch (USB_GET_STATE(xfer)) {
	case USB_ST_TRANSFERRED:
		/* Allocate new mbuf */
		MGETHDR(m, M_NOWAIT, MT_DATA);
		if (m == NULL) {
			UBT_STAT_IERROR(sc);
			goto submit_next;
		}

		MCLGET(m, M_NOWAIT);
		if (!(m->m_flags & M_EXT)) {
			UBT_STAT_IERROR(sc);
			goto submit_next;
		}

		/* Add HCI packet type */
		*mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT;
		m->m_pkthdr.len = m->m_len = 1;

		if (actlen > MCLBYTES - 1)
			actlen = MCLBYTES - 1;

		pc = usbd_xfer_get_frame(xfer, 0);
		usbd_copy_out(pc, 0, mtod(m, uint8_t *) + 1, actlen);
		m->m_pkthdr.len += actlen;
		m->m_len += actlen;

		UBT_INFO(sc, "got %d bytes from bulk-in pipe\n",
			actlen);

		/* Validate packet and send it up the stack */
		if (m->m_pkthdr.len < (int)sizeof(*hdr)) {
			UBT_INFO(sc, "HCI ACL packet is too short\n");

			UBT_STAT_IERROR(sc);
			goto submit_next;
		}

		hdr = mtod(m, ng_hci_acldata_pkt_t *);
		len = le16toh(hdr->length);
		if (len != (int)(m->m_pkthdr.len - sizeof(*hdr))) {
			UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \
				"pktlen=%d\n", len, m->m_pkthdr.len);

			UBT_STAT_IERROR(sc);
			goto submit_next;
		}

		UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \
			"length=%d\n", m->m_pkthdr.len, len);

		UBT_STAT_PCKTS_RECV(sc);
		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);

		ubt_fwd_mbuf_up(sc, &m);
		/* m == NULL at this point */
		/* FALLTHOUGH */

	case USB_ST_SETUP:
submit_next:
		NG_FREE_M(m); /* checks for m != NULL */

		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
		usbd_transfer_submit(xfer);
		break;

	default: /* Error */
		if (error != USB_ERR_CANCELLED) {
			UBT_WARN(sc, "bulk-in transfer failed: %s\n",
				usbd_errstr(error));

			/* Try to clear stall first */
			usbd_xfer_set_stall(xfer);
			goto submit_next;
		}
			/* transfer cancelled */
		break;
	}
} /* ubt_bulk_read_callback */

/*
 * Called when outgoing bulk transfer (ACL packet) has completed, i.e.
 * ACL packet was sent to the device.
 * USB context.
 */

static void
ubt_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
{
	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
	struct mbuf		*m;
	struct usb_page_cache	*pc;
	int			actlen;

	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);

	switch (USB_GET_STATE(xfer)) {
	case USB_ST_TRANSFERRED:
		UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", actlen);
		UBT_STAT_BYTES_SENT(sc, actlen);
		UBT_STAT_PCKTS_SENT(sc);
		/* FALLTHROUGH */

	case USB_ST_SETUP:
send_next:
		/* Get next mbuf, if any */
		UBT_NG_LOCK(sc);
		NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m);
		UBT_NG_UNLOCK(sc);

		if (m == NULL) {
			UBT_INFO(sc, "ACL data queue is empty\n");
			break; /* transfer completed */
		}

		/*
		 * Copy ACL data frame back to a linear USB transfer buffer
		 * and schedule transfer
		 */

		pc = usbd_xfer_get_frame(xfer, 0);
		usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
		usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len);

		UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n",
			m->m_pkthdr.len);

		NG_FREE_M(m);

		usbd_transfer_submit(xfer);
		break;

	default: /* Error */
		if (error != USB_ERR_CANCELLED) {
			UBT_WARN(sc, "bulk-out transfer failed: %s\n",
				usbd_errstr(error));

			UBT_STAT_OERROR(sc);

			/* try to clear stall first */
			usbd_xfer_set_stall(xfer);
			goto send_next;
		}
			/* transfer cancelled */
		break;
	}
} /* ubt_bulk_write_callback */

/*
 * Called when incoming isoc transfer (SCO packet) has completed, i.e.
 * SCO packet was received from the device.
 * USB context.
 */

static void
ubt_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error)
{
	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
	int			n;
	int actlen, nframes;

	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);

	switch (USB_GET_STATE(xfer)) {
	case USB_ST_TRANSFERRED:
		for (n = 0; n < nframes; n ++)
			if (ubt_isoc_read_one_frame(xfer, n) < 0)
				break;
		/* FALLTHROUGH */

	case USB_ST_SETUP:
read_next:
		for (n = 0; n < nframes; n ++)
			usbd_xfer_set_frame_len(xfer, n,
			    usbd_xfer_max_framelen(xfer));

		usbd_transfer_submit(xfer);
		break;

	default: /* Error */
                if (error != USB_ERR_CANCELLED) {
                        UBT_STAT_IERROR(sc);
                        goto read_next;
                }

		/* transfer cancelled */
		break;
	}
} /* ubt_isoc_read_callback */

/*
 * Helper function. Called from ubt_isoc_read_callback() to read
 * SCO data from one frame.
 * USB context.
 */

static int
ubt_isoc_read_one_frame(struct usb_xfer *xfer, int frame_no)
{
	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
	struct usb_page_cache	*pc;
	struct mbuf		*m;
	int			len, want, got, total;

	/* Get existing SCO reassembly buffer */
	pc = usbd_xfer_get_frame(xfer, 0);
	m = sc->sc_isoc_in_buffer;
	total = usbd_xfer_frame_len(xfer, frame_no);

	/* While we have data in the frame */
	while (total > 0) {
		if (m == NULL) {
			/* Start new reassembly buffer */
			MGETHDR(m, M_NOWAIT, MT_DATA);
			if (m == NULL) {
				UBT_STAT_IERROR(sc);
				return (-1);	/* XXX out of sync! */
			}

			MCLGET(m, M_NOWAIT);
			if (!(m->m_flags & M_EXT)) {
				UBT_STAT_IERROR(sc);
				NG_FREE_M(m);
				return (-1);	/* XXX out of sync! */
			}

			/* Expect SCO header */
			*mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT;
			m->m_pkthdr.len = m->m_len = got = 1;
			want = sizeof(ng_hci_scodata_pkt_t);
		} else {
			/*
			 * Check if we have SCO header and if so
			 * adjust amount of data we want
			 */
			got = m->m_pkthdr.len;
			want = sizeof(ng_hci_scodata_pkt_t);

			if (got >= want)
				want += mtod(m, ng_hci_scodata_pkt_t *)->length;
		}

		/* Append frame data to the SCO reassembly buffer */
		len = total;
		if (got + len > want)
			len = want - got;

		usbd_copy_out(pc, frame_no * usbd_xfer_max_framelen(xfer),
			mtod(m, uint8_t *) + m->m_pkthdr.len, len);

		m->m_pkthdr.len += len;
		m->m_len += len;
		total -= len;

		/* Check if we got everything we wanted, if not - continue */
		if (got != want)
			continue;

		/* If we got here then we got complete SCO frame */
		UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \
			"length=%d\n", m->m_pkthdr.len,
			mtod(m, ng_hci_scodata_pkt_t *)->length);

		UBT_STAT_PCKTS_RECV(sc);
		UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);

		ubt_fwd_mbuf_up(sc, &m);
		/* m == NULL at this point */
	}

	/* Put SCO reassembly buffer back */
	sc->sc_isoc_in_buffer = m;

	return (0);
} /* ubt_isoc_read_one_frame */

/*
 * Called when outgoing isoc transfer (SCO packet) has completed, i.e.
 * SCO packet was sent to the device.
 * USB context.
 */

static void
ubt_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error)
{
	struct ubt_softc	*sc = usbd_xfer_softc(xfer);
	struct usb_page_cache	*pc;
	struct mbuf		*m;
	int			n, space, offset;
	int			actlen, nframes;

	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
	pc = usbd_xfer_get_frame(xfer, 0);

	switch (USB_GET_STATE(xfer)) {
	case USB_ST_TRANSFERRED:
		UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", actlen);
		UBT_STAT_BYTES_SENT(sc, actlen);
		UBT_STAT_PCKTS_SENT(sc);
		/* FALLTHROUGH */

	case USB_ST_SETUP:
send_next:
		offset = 0;
		space = usbd_xfer_max_framelen(xfer) * nframes;
		m = NULL;

		while (space > 0) {
			if (m == NULL) {
				UBT_NG_LOCK(sc);
				NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m);
				UBT_NG_UNLOCK(sc);

				if (m == NULL)
					break;
			}

			n = min(space, m->m_pkthdr.len);
			if (n > 0) {
				usbd_m_copy_in(pc, offset, m,0, n);
				m_adj(m, n);

				offset += n;
				space -= n;
			}

			if (m->m_pkthdr.len == 0)
				NG_FREE_M(m); /* sets m = NULL */
		}

		/* Put whatever is left from mbuf back on queue */
		if (m != NULL) {
			UBT_NG_LOCK(sc);
			NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m);
			UBT_NG_UNLOCK(sc);
		}

		/*
		 * Calculate sizes for isoc frames.
		 * Note that offset could be 0 at this point (i.e. we have
		 * nothing to send). That is fine, as we have isoc. transfers
		 * going in both directions all the time. In this case it
		 * would be just empty isoc. transfer.
		 */

		for (n = 0; n < nframes; n ++) {
			usbd_xfer_set_frame_len(xfer, n,
			    min(offset, usbd_xfer_max_framelen(xfer)));
			offset -= usbd_xfer_frame_len(xfer, n);
		}

		usbd_transfer_submit(xfer);
		break;

	default: /* Error */
		if (error != USB_ERR_CANCELLED) {
			UBT_STAT_OERROR(sc);
			goto send_next;
		}

		/* transfer cancelled */
		break;
	}
}

/*
 * Utility function to forward provided mbuf upstream (i.e. up the stack).
 * Modifies value of the mbuf pointer (sets it to NULL).
 * Save to call from any context.
 */

static int
ubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m)
{
	hook_p	hook;
	int	error;

	/*
	 * Close the race with Netgraph hook newhook/disconnect methods.
	 * Save the hook pointer atomically. Two cases are possible:
	 *
	 * 1) The hook pointer is NULL. It means disconnect method got
	 *    there first. In this case we are done.
	 *
	 * 2) The hook pointer is not NULL. It means that hook pointer
	 *    could be either in valid or invalid (i.e. in the process
	 *    of disconnect) state. In any case grab an extra reference
	 *    to protect the hook pointer.
	 *
	 * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as
	 * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY().
	 */

	UBT_NG_LOCK(sc);
	if ((hook = sc->sc_hook) != NULL)
		NG_HOOK_REF(hook);
	UBT_NG_UNLOCK(sc);

	if (hook == NULL) {
		NG_FREE_M(*m);
		return (ENETDOWN);
	}

	NG_SEND_DATA_ONLY(error, hook, *m);
	NG_HOOK_UNREF(hook);

	if (error != 0)
		UBT_STAT_IERROR(sc);

	return (error);
} /* ubt_fwd_mbuf_up */

/****************************************************************************
 ****************************************************************************
 **                                 Glue
 ****************************************************************************
 ****************************************************************************/

/*
 * Schedule glue task. Should be called with sc_ng_mtx held.
 * Netgraph context.
 */

static void
ubt_task_schedule(ubt_softc_p sc, int action)
{
	mtx_assert(&sc->sc_ng_mtx, MA_OWNED);

	/*
	 * Try to handle corner case when "start all" and "stop all"
	 * actions can both be set before task is executed.
	 *
	 * The rules are
	 *
	 * sc_task_flags	action		new sc_task_flags
	 * ------------------------------------------------------
	 * 0			start		start
	 * 0			stop		stop
	 * start		start		start
	 * start		stop		stop
	 * stop			start		stop|start
	 * stop			stop		stop
	 * stop|start		start		stop|start
	 * stop|start		stop		stop
	 */

	if (action != 0) {
		if ((action & UBT_FLAG_T_STOP_ALL) != 0)
			sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL;

		sc->sc_task_flags |= action;
	}

	if (sc->sc_task_flags & UBT_FLAG_T_PENDING)
		return;

	if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) {
		sc->sc_task_flags |= UBT_FLAG_T_PENDING;
		return;
	}

	/* XXX: i think this should never happen */
} /* ubt_task_schedule */

/*
 * Glue task. Examines sc_task_flags and does things depending on it.
 * Taskqueue context.
 */

static void
ubt_task(void *context, int pending)
{
	ubt_softc_p	sc = context;
	int		task_flags, i;

	UBT_NG_LOCK(sc);
	task_flags = sc->sc_task_flags;
	sc->sc_task_flags = 0;
	UBT_NG_UNLOCK(sc);

	/*
	 * Stop all USB transfers synchronously.
	 * Stop interface #0 and #1 transfers at the same time and in the
	 * same loop. usbd_transfer_drain() will do appropriate locking.
	 */

	if (task_flags & UBT_FLAG_T_STOP_ALL)
		for (i = 0; i < UBT_N_TRANSFER; i ++)
			usbd_transfer_drain(sc->sc_xfer[i]);

	/* Start incoming interrupt and bulk, and all isoc. USB transfers */
	if (task_flags & UBT_FLAG_T_START_ALL) {
		/*
		 * Interface #0
		 */

		mtx_lock(&sc->sc_if_mtx);

		ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD);
		ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD);

		/*
		 * Interface #1
		 * Start both read and write isoc. transfers by default.
		 * Get them going all the time even if we have nothing
		 * to send to avoid any delays.
		 */

		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1);
		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2);
		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1);
		ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2);

		mtx_unlock(&sc->sc_if_mtx);
	}

 	/* Start outgoing control transfer */
	if (task_flags & UBT_FLAG_T_START_CTRL) {
		mtx_lock(&sc->sc_if_mtx);
		ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR);
		mtx_unlock(&sc->sc_if_mtx);
	}

	/* Start outgoing bulk transfer */
	if (task_flags & UBT_FLAG_T_START_BULK) {
		mtx_lock(&sc->sc_if_mtx);
		ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR);
		mtx_unlock(&sc->sc_if_mtx);
	}
} /* ubt_task */

/****************************************************************************
 ****************************************************************************
 **                        Netgraph specific
 ****************************************************************************
 ****************************************************************************/

/*
 * Netgraph node constructor. Do not allow to create node of this type.
 * Netgraph context.
 */

static int
ng_ubt_constructor(node_p node)
{
	return (EINVAL);
} /* ng_ubt_constructor */

/*
 * Netgraph node destructor. Destroy node only when device has been detached.
 * Netgraph context.
 */

static int
ng_ubt_shutdown(node_p node)
{
	if (node->nd_flags & NGF_REALLY_DIE) {
		/*
                 * We came here because the USB device is being
		 * detached, so stop being persistant.
                 */
		NG_NODE_SET_PRIVATE(node, NULL);
		NG_NODE_UNREF(node);
	} else
		NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */

	return (0);
} /* ng_ubt_shutdown */

/*
 * Create new hook. There can only be one.
 * Netgraph context.
 */

static int
ng_ubt_newhook(node_p node, hook_p hook, char const *name)
{
	struct ubt_softc	*sc = NG_NODE_PRIVATE(node);

	if (strcmp(name, NG_UBT_HOOK) != 0)
		return (EINVAL);

	UBT_NG_LOCK(sc);
	if (sc->sc_hook != NULL) {
		UBT_NG_UNLOCK(sc);

		return (EISCONN);
	}

	sc->sc_hook = hook;
	UBT_NG_UNLOCK(sc);

	return (0);
} /* ng_ubt_newhook */

/*
 * Connect hook. Start incoming USB transfers.
 * Netgraph context.
 */

static int
ng_ubt_connect(hook_p hook)
{
	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));

	NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));

	UBT_NG_LOCK(sc);
	ubt_task_schedule(sc, UBT_FLAG_T_START_ALL);
	UBT_NG_UNLOCK(sc);

	return (0);
} /* ng_ubt_connect */

/*
 * Disconnect hook.
 * Netgraph context.
 */

static int
ng_ubt_disconnect(hook_p hook)
{
	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));

	UBT_NG_LOCK(sc);

	if (hook != sc->sc_hook) {
		UBT_NG_UNLOCK(sc);

		return (EINVAL);
	}

	sc->sc_hook = NULL;

	/* Kick off task to stop all USB xfers */
	ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL);

	/* Drain queues */
	NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
	NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
	NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);

	UBT_NG_UNLOCK(sc);

	return (0);
} /* ng_ubt_disconnect */

/*
 * Process control message.
 * Netgraph context.
 */

static int
ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)
{
	struct ubt_softc	*sc = NG_NODE_PRIVATE(node);
	struct ng_mesg		*msg, *rsp = NULL;
	struct ng_bt_mbufq	*q;
	int			error = 0, queue, qlen;

	NGI_GET_MSG(item, msg);

	switch (msg->header.typecookie) {
	case NGM_GENERIC_COOKIE:
		switch (msg->header.cmd) {
		case NGM_TEXT_STATUS:
			NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT);
			if (rsp == NULL) {
				error = ENOMEM;
				break;
			}

			snprintf(rsp->data, NG_TEXTRESPONSE,
				"Hook: %s\n" \
				"Task flags: %#x\n" \
				"Debug: %d\n" \
				"CMD queue: [have:%d,max:%d]\n" \
				"ACL queue: [have:%d,max:%d]\n" \
				"SCO queue: [have:%d,max:%d]",
				(sc->sc_hook != NULL) ? NG_UBT_HOOK : "",
				sc->sc_task_flags,
				sc->sc_debug,
				sc->sc_cmdq.len,
				sc->sc_cmdq.maxlen,
				sc->sc_aclq.len,
				sc->sc_aclq.maxlen,
				sc->sc_scoq.len,
				sc->sc_scoq.maxlen);
			break;

		default:
			error = EINVAL;
			break;
		}
		break;

	case NGM_UBT_COOKIE:
		switch (msg->header.cmd) {
		case NGM_UBT_NODE_SET_DEBUG:
			if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){
				error = EMSGSIZE;
				break;
			}

			sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data));
			break;

		case NGM_UBT_NODE_GET_DEBUG:
			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep),
			    M_NOWAIT);
			if (rsp == NULL) {
				error = ENOMEM;
				break;
			}

			*((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug;
			break;

		case NGM_UBT_NODE_SET_QLEN:
			if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
				error = EMSGSIZE;
				break;
			}

			queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
			qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen;

			switch (queue) {
			case NGM_UBT_NODE_QUEUE_CMD:
				q = &sc->sc_cmdq;
				break;

			case NGM_UBT_NODE_QUEUE_ACL:
				q = &sc->sc_aclq;
				break;

			case NGM_UBT_NODE_QUEUE_SCO:
				q = &sc->sc_scoq;
				break;

			default:
				error = EINVAL;
				goto done;
				/* NOT REACHED */
			}

			q->maxlen = qlen;
			break;

		case NGM_UBT_NODE_GET_QLEN:
			if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
				error = EMSGSIZE;
				break;
			}

			queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;

			switch (queue) {
			case NGM_UBT_NODE_QUEUE_CMD:
				q = &sc->sc_cmdq;
				break;

			case NGM_UBT_NODE_QUEUE_ACL:
				q = &sc->sc_aclq;
				break;

			case NGM_UBT_NODE_QUEUE_SCO:
				q = &sc->sc_scoq;
				break;

			default:
				error = EINVAL;
				goto done;
				/* NOT REACHED */
			}

			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep),
				M_NOWAIT);
			if (rsp == NULL) {
				error = ENOMEM;
				break;
			}

			((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue;
			((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen;
			break;

		case NGM_UBT_NODE_GET_STAT:
			NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep),
			    M_NOWAIT);
			if (rsp == NULL) {
				error = ENOMEM;
				break;
			}

			bcopy(&sc->sc_stat, rsp->data,
				sizeof(ng_ubt_node_stat_ep));
			break;

		case NGM_UBT_NODE_RESET_STAT:
			UBT_STAT_RESET(sc);
			break;

		default:
			error = EINVAL;
			break;
		}
		break;

	default:
		error = EINVAL;
		break;
	}
done:
	NG_RESPOND_MSG(error, node, item, rsp);
	NG_FREE_MSG(msg);

	return (error);
} /* ng_ubt_rcvmsg */

/*
 * Process data.
 * Netgraph context.
 */

static int
ng_ubt_rcvdata(hook_p hook, item_p item)
{
	struct ubt_softc	*sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
	struct mbuf		*m;
	struct ng_bt_mbufq	*q;
	int			action, error = 0;

	if (hook != sc->sc_hook) {
		error = EINVAL;
		goto done;
	}

	/* Deatch mbuf and get HCI frame type */
	NGI_GET_M(item, m);

	/*
	 * Minimal size of the HCI frame is 4 bytes: 1 byte frame type,
	 * 2 bytes connection handle and at least 1 byte of length.
	 * Panic on data frame that has size smaller than 4 bytes (it
	 * should not happen)
	 */

	if (m->m_pkthdr.len < 4)
		panic("HCI frame size is too small! pktlen=%d\n",
			m->m_pkthdr.len);

	/* Process HCI frame */
	switch (*mtod(m, uint8_t *)) {	/* XXX call m_pullup ? */
	case NG_HCI_CMD_PKT:
		if (m->m_pkthdr.len - 1 > (int)UBT_CTRL_BUFFER_SIZE)
			panic("HCI command frame size is too big! " \
				"buffer size=%zd, packet len=%d\n",
				UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len);

		q = &sc->sc_cmdq;
		action = UBT_FLAG_T_START_CTRL;
		break;

	case NG_HCI_ACL_DATA_PKT:
		if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE)
			panic("ACL data frame size is too big! " \
				"buffer size=%d, packet len=%d\n",
				UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len);

		q = &sc->sc_aclq;
		action = UBT_FLAG_T_START_BULK;
		break;

	case NG_HCI_SCO_DATA_PKT:
		q = &sc->sc_scoq;
		action = 0;
		break;

	default:
		UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \
			"pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len);

		NG_FREE_M(m);
		error = EINVAL;
		goto done;
		/* NOT REACHED */
	}

	UBT_NG_LOCK(sc);
	if (NG_BT_MBUFQ_FULL(q)) {
		NG_BT_MBUFQ_DROP(q);
		UBT_NG_UNLOCK(sc);

		UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n",
			*mtod(m, uint8_t *), m->m_pkthdr.len);

		NG_FREE_M(m);
	} else {
		/* Loose HCI packet type, enqueue mbuf and kick off task */
		m_adj(m, sizeof(uint8_t));
		NG_BT_MBUFQ_ENQUEUE(q, m);
		ubt_task_schedule(sc, action);
		UBT_NG_UNLOCK(sc);
	}
done:
	NG_FREE_ITEM(item);

	return (error);
} /* ng_ubt_rcvdata */

/****************************************************************************
 ****************************************************************************
 **                              Module
 ****************************************************************************
 ****************************************************************************/

/*
 * Load/Unload the driver module
 */

static int
ubt_modevent(module_t mod, int event, void *data)
{
	int	error;

	switch (event) {
	case MOD_LOAD:
		error = ng_newtype(&typestruct);
		if (error != 0)
			printf("%s: Could not register Netgraph node type, " \
				"error=%d\n", NG_UBT_NODE_TYPE, error);
		break;

	case MOD_UNLOAD:
		error = ng_rmtype(&typestruct);
		break;

	default:
		error = EOPNOTSUPP;
		break;
	}

	return (error);
} /* ubt_modevent */

static devclass_t	ubt_devclass;

static device_method_t	ubt_methods[] =
{
	DEVMETHOD(device_probe,	ubt_probe),
	DEVMETHOD(device_attach, ubt_attach),
	DEVMETHOD(device_detach, ubt_detach),
	DEVMETHOD_END
};

static driver_t		ubt_driver =
{
	.name =	   "ubt",
	.methods = ubt_methods,
	.size =	   sizeof(struct ubt_softc),
};

DRIVER_MODULE(ng_ubt, uhub, ubt_driver, ubt_devclass, ubt_modevent, 0);
MODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION);
MODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION);
MODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
MODULE_DEPEND(ng_ubt, usb, 1, 1, 1);

redpajama_set_name: RedPajamaGithub


Dolley Madison was the wife of President James Madiosn. Her greatest legacy is establishing the idea that a First Lady should serve as the mother of a nation. She served as hostess of the White House by hosting social functions and establishing the chief role of the First Lady.


Crystal Dynamics has announced Rise of the Tomb Raider, coming Holiday 2015. A trailer was shown during Microsoft's E3 press conference featured a distressed Lara undergoing therapy after the events of 2012's Tomb Raider. Lara Coft is also seen going through massive caves and killing enemies skillfully with her bow. The game looks very exciting and another game to add to your anticipation list.
More tomb raider is good. The reboot was phenomenal.