It is apparent that you have been looking forward to your wedding day for the rest of your life. It means that you cannot afford not to capture everything that will happen during the special occasion when you exchange vows with the love of your life. Do not think that you can afford to ask your friend or relative to take the photographs during the wedding since they may not deliver quality services. The perfect approach is engaging the expert wedding photographer in the market since they have all that it takes to ensure that you will get photos with the highest quality. Deciding the best wedding photographer is not a stroll in the zoo work for you because of the numerous professionals in the market. The article concentrates on the tips that should be assistive when finding the perfect Yorkshire wedding photographer.
Experience is one of the things that you cannot afford to overlook when deciding the most excellent wedding photographers in the market. Attest to it that you will know the period that the photographer has been in the sector before you engage them for the job. Furthermore, you must see to it that you will know the wedding events that the photographer in question has worked for previously.
Have the zeal to wish to have a view of the photos that the wedding photographer in question took in the past before you engage them for the job. Ensure that you will hire Simon Hogben Photography after you attest to it that you are contented with the standard of their previous services. Besides, you can call some of their former clients who will tell you if they were happy with the standard of their services.
Do not forget that a photographer can have some inconveniences that can make them not to turn up for your event. There is no doubt that the last thing you will want is to go for the wedding only to realize that no one is there to take the photos. It is for this cause that you must verify that you will engage the photographer who has a plan B in case they have some inconveniences.
Lastly, do not forget to consider the amount of cash you will have to pay for the services of the wedding photographer. Ensure that you will take your wedding photography budget into account when you are looking for the perfect professional for the job. The best photographer is one whose services are affordable for you.
See more tips on this link: https://www.huffpost.com/entry/6-creative-ways-to-preser_b_12554428.


Features extensive collection of presidential trivia.
Online quizzes about past U.S. presidents and discussion ideas for families.
Presidents' Day or Washington's Birthday?
Describes the development of the holiday, links to presidential and first lady biographies, and trivia.


As you know festival season will be upon us in no time and so we need to start preparing our wardrobes! Good old British weather is so un predictable so you need to be prepared for anything, unlike the beautiful weather they just had in California for the annual Coachella festival which looked amazing! Not to mention the outfits it seems like anything goes!
Dont forget our crotchet 2 piece available in black and white will be on the website soon ready to make an appearance at a festival or two!
Our favorite kardashians sisters certainly made statements with there outfits which are bang on trend what do you think??
Denim also a huge hit this year especially double denim and we're pretty sure most of you will have a denim coat hidden away somewhere well its time to get it out!! The great thing about denim is you can wear it with anything and in any way we love to wear a pair of converse, you could wear a cute little pair of boots or a gorgeous pair of gladiator sandals, tall or short you decide!
Do these girls ever get it wrong??
Last but not least the dresses most definitely boho oversized, long, short such a chilled out outfit which can be really fun to wear, and any footwear goes love the cow boy boots!
Listening to the radio as i write this and they're talking about Glastonbury must be a sign! If your off to a festival this year have fun! hopefully it will be dry and hopefully this blog has given you some inspiration of what to take with you to keep you festival fabulous!!


text:
The fallout from Prince Harry and Meghan Markles incendiary Oprah Winfrey interview continues, with Piers Morgan leaving Good Morning Britain following comments he made about Meghan.Buckingham Palace said the issues raised in the interview, especially over race, were concerning and would be addressed by the royal family privately.Morgan left the ITV show on Tuesday after an on-air row with weather presenter Alex Beresford, who criticised him for continuing to trash the duchess.
He faces an Ofcom investigation after the regulator received 41,000 complaints when he said on Monday he didnt believe a word the royal said during her interview.
Piers Morgan (Kirsty O'Connor/PA)
The royal family said it was taking very seriously Harry and Meghans shocking allegations of racism and a lack of support, despite some recollections among royals varying.
Meanwhile, American broadcaster CBS said nearly 50 million people have watched the two-hour interview worldwide, with the figure continuing to grow due to availability on streaming services.
The Queen has taken the first step in healing divisions after the couples interview was aired to UK audiences on Monday by publicly acknowledging the challenging period they experienced when they were working royals.
The family appear to be at odds over the version of some events described by the Sussexes, but the sympathetic tone of a Buckingham Palace statement suggests a reconciliation through dialogue in private is the aim.
The highly-anticipated palace statement issued on behalf of the Queen also mentioned the couple and their son by their first names and not titles.
The statement said: The whole family is saddened to learn the full extent of how challenging the last few years have been for Harry and Meghan.
The issues raised, particularly that of race, are concerning. Whilst some recollections may vary, they are taken very seriously and will be addressed by the family privately.
Harry, Meghan and Archie will always be much-loved family members.
The palace statement will be an attempt to draw a line under a series of bombshell claims made by the couple that have thrown the monarchy into a crisis that threatens to damage its reputation around the world.
The royal family are expected to resolve their problems in private (Phil Harris/Daily Mirror/PA)
There was mounting criticism about the picture painted by Harry and Meghan of an uncaring and racist institution that failed to support the couple, especially as the duchess struggled with her mental health.
Plagued by suicidal thoughts, Meghan received no help from the monarchy after approaching staff even the human resources department.
Winfrey was left open-mouthed when the couple recounted how a family member not the Queen or Prince Philip raised concerns about how dark their son Archies skin tone might be before he was born.
There were also revelations about the rift between Harry and his father, Prince Charles, and claims he was financially cut off by the family just as he was about to embark on his new life with Meghan and son Archie in North America.
Charles chuckled nervously when he was asked if he had seen Meghan and Harrys interview with Winfrey during a visit to a pop-up Covid vaccination clinic at a London church.
Harry and Meghan during their interview with Winfrey (Joe Pugliese/Harpo Productions/PA)
The Evening Standard reported a senior royal household figure as suggesting the heir to the throne felt let down by what the couple had said.
The source added: The prince is proud of the diversity of this country and believes the diversity of Britains modern society is its greatest strength.
There has been speculation about the length of time it took Buckingham Palace to release its statement just before 5.30pm on Tuesday and more than 36 hours since the Winfrey interview was screened in America.
But the delay likely allowed for all senior members of the royal family on both sides of the Atlantic time to join discussions and the British public to watch the full programme aired in the UK on Monday night.
It is understood the response was also timed to allow for a carefully considered reaction.
The Queen and the rest of the royal family are said to consider this a family matter and believe they should be given the opportunity to raise the issues privately.
The monarch and aides from all royal households will have been in crisis summits working out how to respond to the damaging allegations.
Previous PostMANILA – The Aboitiz Group has announced it will cover the inoculation of all its team members and subcontractors to help protect them from the coronavirus disease 2019 (Covid-19). AstraZeneca Covid-19 vaccines will be offered to more than 30,000 regular empl…
Next Post"He is without doubt an outspoken, challenging, opinionated, disruptive broadcaster."
TheJournal.ie supports the work of the Press Council of Ireland and the Office of the Press Ombudsman, and our staff...
Veteran space scientist G Madhavan Nair expressed shock over the unsuccessful GSLV-F10 launch mission but said the ISRO is resilient enough to bounce back.
ISRO today launched the GSLV-F10 rocket with earth observation satellite EOS-03 on board Bengaluru: Veteran space scientist G Madhavan Nair...
Millions across Australia are in lockdown and NSW is on the verge of disaster, but it hasn't been enough to drive down vaccine hesitancy.
University of Melbourne epidemiologist Nancy Baxter has warned that Greater Sydney's outbreak is about to see "large increases" in case...
PUTRAJAYA: The Malaysia My Second Home (MM2H) programme has been reactivated with several changes made to the policy.
PUTRAJAYA: The Malaysia My Second Home (MM2H) programme has been reactivated with several changes made to the policy. The decision...

redpajama_set_name: RedPajamaCommonCrawl


As I had manually created an uploads directory I had to reassign group ownership to allow me to upload content to that directory using the following.
Hey presto! we are now running on the latest version, with all existing pluggins and content still working. (I double-checked by running wpscan from my kali box just to be sure I was on the latest version) Hope this helps someone else out. Don't forget to backup before running these steps.


MUMBAI: Discovery Communications India (DCIN), country's leading factual entertainment network, has entered into a unique partnership with India's No 1 News and local content discovery app – Dailyhunt. DCIN has introduced a video destination named Discovery Plus on the home page of Dailyhunt App offering exhilarating short form video content especially customized for the digital audiences in India. The content across Discovery's strong verticals of Outdoor, Food, Wildlife, Science and Military is currently available in five languages namely English, Hindi, Tamil and Telugu and Bengali.
This win-win partnership will help DCIN immediately expand its footprint on the digital domain in India leveraging 153 million monthly user base of Dailyhunt platform. On the other hand, Dailyhunt will be enriched with the unrivalled content library and upcoming titles of Discovery thus helping nurture passionate communities looking for factual entertainment on digital media.
Within 9 days of the soft launch, the Discovery Plus section has received more than 50 million views and more than 4 million unique users visited the Discovery Plus section on the Dailyhunt app. DCIN aims to refresh videos on Discovery Plus, ranging from 1-5 minutes, on a daily basis to engage with the emerging superfans community.


MILAN — An array of crises will keep several world leaders away from the annual World Economic Forum in Davos next week, which takes place against a backdrop of deepening gloom over the global economic and political outlook.
Some 3,000 business, government and civil society figures are due to gather in the snow-blanketed ski resort, but among them are only three leaders of the Group of Seven most industrialized countries: Japanese Prime Minister Shinzo Abe, German Chancellor Angela Merkel and Italian Premier Giuseppe Conte.
Outside the G7, the leaders of Russia and India are shunning Davos, while China – whose president, Xi Jinping, was the first Chinese leader to attend the elite gathering in 2017 to offer a vigorous defense of free trade – is sending his deputy instead.
"It's the best place to pitch for ideas, build connections and get your brand known," said Chen Linchevski, chief executive of Precognize, an Israel-based start-up developing software that prevents technical or quality failures at manufacturing plants. "It's the kind of place where in a few days you meet people you wouldn't easily meet otherwise," said Linchevski, who is paying 50,000 Swiss francs (US$50,495) to attend the event.


We have reserved a block of rooms for our guests at the Embassy Suites Philadelphia - Center City for $129 per night. It's one-third of a mile away from the library (a 5 minute walk; click here for a map). All suites are identical (aside from choice of beds: king or 2 doubles) and include a living room with sleeper sofa, microwave, refrigerator, etc. Breakfast buffet is complimentary.
Call 215-561-1776 and mention you're attending the Gilewski-Nguyen wedding or click here to reserve online. You may also want to check the current price to see if it's better than our reserved price.
If you wish to stay near Duc's parents' house in Galloway, New Jersey, there are a number of choices just a few miles away. We will update this section if a consensus is formed on where our families should stay.


Souls Harbour Rescue Mission assists people affected by poverty and addiction by offering emergency help such as food, clothing, shelter, recovery programs and the message of hope. They have a free clothing and household goods store, a drop-in centre where they offer peer and pastoral counseling and serve 200 hot, nutritious meals 6 days a week.
Hope Air is a registered national charity that provides free flights for people across Canada who need medical expertise and technologies that are only available in larger centres. Through this organization, commercial and private pilots volunteer their time, aircraft and expenses to help clients who physically or financially cannot afford to travel for medical care.
The IWK Foundation Sea Star Child and Youth Advocacy brings all professionals and resources for abused children together in one familiar and comfortable child and family-friendly environment. The healing process can move forward for the whole family, leading to better outcomes for children, youth and their caregivers.
Baby It's Cold Outside has been operating for over 12 years, and has seen an increase in demand because of the struggling Alberta economy. By organizing food and clothing drives and facilitating distribution to homeless shelters, this charity gives a hand up to people who need it most!
By providing year-round sports and recreation programs specialized to support persons with Autism Spectrum Disorder, Canucks Autism Network creates a barrier-free, inclusive and supportive environment for children, teens, young adults and families.
Through the Never Alone Foundation, volunteers offer love and support for people living with cancer. They also share everything from gift certificates and blankets to advice about available services to help improve the quality of someone's life. Funds raised go beyond the bedside to revitalize and improve palliative care and treatment facilities and provide respite for patients and their families.
Ronald McDonald House Charities give families with children in medical care a place to stay, complete with food and bedding and everything you would find at home, in location that is close to the hospital where the child is being treated alleviating the stress of travel. They take care of the day-to-day operations of a household, so the family can focus on the child's care.
La Rose des Vents assists victims of abuse and provides programs for women, young adults and teenagers to help break the cycle of violence. They help women and children take control of their lives by teaching the importance of self-respect and helping them regain self-esteem and confidence.
Art City is a community art studio where kids of all ages and walks of life can work with professional artists! Through their programs, they learn about the environment, re-purpose items, work on cleanup and neighborhood beautification projects and empower youth through relationship building and a sense of ownership and pride in the community!
Choose Life Ministry is a place for young women between the ages of 18 and 29 to find freedom from life-altering challenges including substance abuse, unexpected pregnancy, the effects of physical and/or sexual abuse, mental illness, such as depression and anxiety, as well as challenges such as eating disorders and self-harm behaviours.
The Coping Centre provides supportive programs and services for families and individuals who have lost a loved one, helping them in a time of need.
Terra Centre provides supports and services that empower pregnant and parenting teens to become self-reliant and make positive choices and develop the skills to be successful parents. Counseling, guidance and informational support is offered in a safe and nurturing environment to promote a healthy future for children and families!
Since opening in 2011, the Edmundston Thrift Store and Food Bank has been distributing food hampers to those in need every week and is devoted to helping the people in their region with kindness and without judgment. This same organization not only offers food bank services, but also used clothing, furniture and appliances at a modest price and even an employment program!
Jacob's Planet Project began as a project for a 9-year-old boy in an engineering class for kids! Jacob learned that hair and fur absorb and retain oil much better than synthetic materials and can be used to cleanup oil spills from waterways! He began to collect hair and fur from local salons and groomers and, using his allowance and earnings, he packed and shipped it to organizations that create the felt mats and booms. Jacob now fundraises to cover the cost of shipping!
The Little Warriors Be Brave Ranch is a place of healing, treatment and support for children 8-12 years of age who are victims of sexual abuse. It is the first and only long-term treatment centre designed specifically for children and offers intensive, multimodal therapy. It truly is a place of healing for children and their families.


text:
#include <linux/module.h>

#include <linux/fd.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/blkdev.h>
#include <linux/mutex.h>
#include <linux/completion.h>
#include <linux/wait.h>

#include <asm/atafd.h>
#include <asm/atafdreg.h>
#include <asm/atariints.h>
#include <asm/atari_stdma.h>
#include <asm/atari_stram.h>

#define	FD_MAX_UNITS 2

#undef DEBUG

static DEFINE_MUTEX(ataflop_mutex);
static struct request *fd_request;
static int fdc_queue;

/* Disk types: DD, HD, ED */
static struct atari_disk_type {
	const char	*name;
	unsigned	spt;		/* sectors per track */
	unsigned	blocks;		/* total number of blocks */
	unsigned	fdc_speed;	/* fdc_speed setting */
	unsigned 	stretch;	/* track doubling ? */
} atari_disk_type[] = {
	{ "d360",  9, 720, 0, 0},	/*  0: 360kB diskette */
	{ "D360",  9, 720, 0, 1},	/*  1: 360kb in 720k or 1.2MB drive */
	{ "D720",  9,1440, 0, 0},	/*  2: 720kb in 720k or 1.2MB drive */
	{ "D820", 10,1640, 0, 0},	/*  3: DD disk with 82 tracks/10 sectors */
/* formats above are probed for type DD */
#define	MAX_TYPE_DD 3
	{ "h1200",15,2400, 3, 0},	/*  4: 1.2MB diskette */
	{ "H1440",18,2880, 3, 0},	/*  5: 1.4 MB diskette (HD) */
	{ "H1640",20,3280, 3, 0},	/*  6: 1.64MB diskette (fat HD) 82 tr 20 sec */
/* formats above are probed for types DD and HD */
#define	MAX_TYPE_HD 6
	{ "E2880",36,5760, 3, 0},	/*  7: 2.8 MB diskette (ED) */
	{ "E3280",40,6560, 3, 0},	/*  8: 3.2 MB diskette (fat ED) 82 tr 40 sec */
/* formats above are probed for types DD, HD and ED */
#define	MAX_TYPE_ED 8
/* types below are never autoprobed */
	{ "H1680",21,3360, 3, 0},	/*  9: 1.68MB diskette (fat HD) 80 tr 21 sec */
	{ "h410",10,820, 0, 1},		/* 10: 410k diskette 41 tr 10 sec, stretch */
	{ "h1476",18,2952, 3, 0},	/* 11: 1.48MB diskette 82 tr 18 sec */
	{ "H1722",21,3444, 3, 0},	/* 12: 1.72MB diskette 82 tr 21 sec */
	{ "h420",10,840, 0, 1},		/* 13: 420k diskette 42 tr 10 sec, stretch */
	{ "H830",10,1660, 0, 0},	/* 14: 820k diskette 83 tr 10 sec */
	{ "h1494",18,2952, 3, 0},	/* 15: 1.49MB diskette 83 tr 18 sec */
	{ "H1743",21,3486, 3, 0},	/* 16: 1.74MB diskette 83 tr 21 sec */
	{ "h880",11,1760, 0, 0},	/* 17: 880k diskette 80 tr 11 sec */
	{ "D1040",13,2080, 0, 0},	/* 18: 1.04MB diskette 80 tr 13 sec */
	{ "D1120",14,2240, 0, 0},	/* 19: 1.12MB diskette 80 tr 14 sec */
	{ "h1600",20,3200, 3, 0},	/* 20: 1.60MB diskette 80 tr 20 sec */
	{ "H1760",22,3520, 3, 0},	/* 21: 1.76MB diskette 80 tr 22 sec */
	{ "H1920",24,3840, 3, 0},	/* 22: 1.92MB diskette 80 tr 24 sec */
	{ "E3200",40,6400, 3, 0},	/* 23: 3.2MB diskette 80 tr 40 sec */
	{ "E3520",44,7040, 3, 0},	/* 24: 3.52MB diskette 80 tr 44 sec */
	{ "E3840",48,7680, 3, 0},	/* 25: 3.84MB diskette 80 tr 48 sec */
	{ "H1840",23,3680, 3, 0},	/* 26: 1.84MB diskette 80 tr 23 sec */
	{ "D800",10,1600, 0, 0},	/* 27: 800k diskette 80 tr 10 sec */
};

static int StartDiskType[] = {
	MAX_TYPE_DD,
	MAX_TYPE_HD,
	MAX_TYPE_ED
};

#define	TYPE_DD		0
#define	TYPE_HD		1
#define	TYPE_ED		2

static int DriveType = TYPE_HD;

static DEFINE_SPINLOCK(ataflop_lock);

/* Array for translating minors into disk formats */
static struct {
	int 	 index;
	unsigned drive_types;
} minor2disktype[] = {
	{  0, TYPE_DD },	/*  1: d360 */
	{  4, TYPE_HD },	/*  2: h1200 */
	{  1, TYPE_DD },	/*  3: D360 */
	{  2, TYPE_DD },	/*  4: D720 */
	{  1, TYPE_DD },	/*  5: h360 = D360 */
	{  2, TYPE_DD },	/*  6: h720 = D720 */
	{  5, TYPE_HD },	/*  7: H1440 */
	{  7, TYPE_ED },	/*  8: E2880 */
/* some PC formats :-) */
	{  8, TYPE_ED },	/*  9: E3280    <- was "CompaQ" == E2880 for PC */
	{  5, TYPE_HD },	/* 10: h1440 = H1440 */
	{  9, TYPE_HD },	/* 11: H1680 */
	{ 10, TYPE_DD },	/* 12: h410  */
	{  3, TYPE_DD },	/* 13: H820     <- == D820, 82x10 */
	{ 11, TYPE_HD },	/* 14: h1476 */
	{ 12, TYPE_HD },	/* 15: H1722 */
	{ 13, TYPE_DD },	/* 16: h420  */
	{ 14, TYPE_DD },	/* 17: H830  */
	{ 15, TYPE_HD },	/* 18: h1494 */
	{ 16, TYPE_HD },	/* 19: H1743 */
	{ 17, TYPE_DD },	/* 20: h880  */
	{ 18, TYPE_DD },	/* 21: D1040 */
	{ 19, TYPE_DD },	/* 22: D1120 */
	{ 20, TYPE_HD },	/* 23: h1600 */
	{ 21, TYPE_HD },	/* 24: H1760 */
	{ 22, TYPE_HD },	/* 25: H1920 */
	{ 23, TYPE_ED },	/* 26: E3200 */
	{ 24, TYPE_ED },	/* 27: E3520 */
	{ 25, TYPE_ED },	/* 28: E3840 */
	{ 26, TYPE_HD },	/* 29: H1840 */
	{ 27, TYPE_DD },	/* 30: D800  */
	{  6, TYPE_HD },	/* 31: H1640    <- was H1600 == h1600 for PC */
};

#define NUM_DISK_MINORS ARRAY_SIZE(minor2disktype)

/*
 * Maximum disk size (in kilobytes). This default is used whenever the
 * current disk size is unknown.
 */
#define MAX_DISK_SIZE 3280

/*
 * MSch: User-provided type information. 'drive' points to
 * the respective entry of this array. Set by FDSETPRM ioctls.
 */
static struct atari_disk_type user_params[FD_MAX_UNITS];

/*
 * User-provided permanent type information. 'drive' points to
 * the respective entry of this array.  Set by FDDEFPRM ioctls,
 * restored upon disk change by floppy_revalidate() if valid (as seen by
 * default_params[].blocks > 0 - a bit in unit[].flags might be used for this?)
 */
static struct atari_disk_type default_params[FD_MAX_UNITS];

/* current info on each unit */
static struct atari_floppy_struct {
	int connected;				/* !=0 : drive is connected */
	int autoprobe;				/* !=0 : do autoprobe	    */

	struct atari_disk_type	*disktype;	/* current type of disk */

	int track;		/* current head position or -1 if
				   unknown */
	unsigned int steprate;	/* steprate setting */
	unsigned int wpstat;	/* current state of WP signal (for
				   disk change detection) */
	int flags;		/* flags */
	struct gendisk *disk;
	int ref;
	int type;
} unit[FD_MAX_UNITS];

#define	UD	unit[drive]
#define	UDT	unit[drive].disktype
#define	SUD	unit[SelectedDrive]
#define	SUDT	unit[SelectedDrive].disktype

#define FDC_READ(reg) ({			\
    /* unsigned long __flags; */		\
    unsigned short __val;			\
    /* local_irq_save(__flags); */		\
    dma_wd.dma_mode_status = 0x80 | (reg);	\
    udelay(25);					\
    __val = dma_wd.fdc_acces_seccount;		\
    MFPDELAY();					\
    /* local_irq_restore(__flags); */		\
    __val & 0xff;				\
})

#define FDC_WRITE(reg,val)			\
    do {					\
	/* unsigned long __flags; */		\
	/* local_irq_save(__flags); */		\
	dma_wd.dma_mode_status = 0x80 | (reg);	\
	udelay(25);				\
	dma_wd.fdc_acces_seccount = (val);	\
	MFPDELAY();				\
        /* local_irq_restore(__flags); */	\
    } while(0)

/* Buffering variables:
 * First, there is a DMA buffer in ST-RAM that is used for floppy DMA
 * operations. Second, a track buffer is used to cache a whole track
 * of the disk to save read operations. These are two separate buffers
 * because that allows write operations without clearing the track buffer.
 */

static int MaxSectors[] = {
	11, 22, 44
};
static int BufferSize[] = {
	15*512, 30*512, 60*512
};

#define	BUFFER_SIZE	(BufferSize[DriveType])

unsigned char *DMABuffer;			  /* buffer for writes */
static unsigned long PhysDMABuffer;   /* physical address */

static int UseTrackbuffer = -1;		  /* Do track buffering? */
module_param(UseTrackbuffer, int, 0);

unsigned char *TrackBuffer;			  /* buffer for reads */
static unsigned long PhysTrackBuffer; /* physical address */
static int BufferDrive, BufferSide, BufferTrack;
static int read_track;		/* non-zero if we are reading whole tracks */

#define	SECTOR_BUFFER(sec)	(TrackBuffer + ((sec)-1)*512)
#define	IS_BUFFERED(drive,side,track) \
    (BufferDrive == (drive) && BufferSide == (side) && BufferTrack == (track))

/*
 * These are global variables, as that's the easiest way to give
 * information to interrupts. They are the data used for the current
 * request.
 */
static int SelectedDrive = 0;
static int ReqCmd, ReqBlock;
static int ReqSide, ReqTrack, ReqSector, ReqCnt;
static int HeadSettleFlag = 0;
static unsigned char *ReqData, *ReqBuffer;
static int MotorOn = 0, MotorOffTrys;
static int IsFormatting = 0, FormatError;

static int UserSteprate[FD_MAX_UNITS] = { -1, -1 };
module_param_array(UserSteprate, int, NULL, 0);

/* Synchronization of FDC access. */
static volatile int fdc_busy = 0;
static DECLARE_WAIT_QUEUE_HEAD(fdc_wait);
static DECLARE_COMPLETION(format_wait);

static unsigned long changed_floppies = 0xff, fake_change = 0;
#define	CHECK_CHANGE_DELAY	HZ/2

#define	FD_MOTOR_OFF_DELAY	(3*HZ)
#define	FD_MOTOR_OFF_MAXTRY	(10*20)

#define FLOPPY_TIMEOUT		(6*HZ)
#define RECALIBRATE_ERRORS	4	/* After this many errors the drive
					 * will be recalibrated. */
#define MAX_ERRORS		8	/* After this many errors the driver
					 * will give up. */

/*
 * The driver is trying to determine the correct media format
 * while Probing is set. fd_rwsec_done() clears it after a
 * successful access.
 */
static int Probing = 0;

/* This flag is set when a dummy seek is necessary to make the WP
 * status bit accessible.
 */
static int NeedSeek = 0;

#ifdef DEBUG
#define DPRINT(a)	printk a
#else
#define DPRINT(a)
#endif

/***************************** Prototypes *****************************/

static void fd_select_side( int side );
static void fd_select_drive( int drive );
static void fd_deselect( void );
static void fd_motor_off_timer( unsigned long dummy );
static void check_change( unsigned long dummy );
static irqreturn_t floppy_irq (int irq, void *dummy);
static void fd_error( void );
static int do_format(int drive, int type, struct atari_format_descr *desc);
static void do_fd_action( int drive );
static void fd_calibrate( void );
static void fd_calibrate_done( int status );
static void fd_seek( void );
static void fd_seek_done( int status );
static void fd_rwsec( void );
static void fd_readtrack_check( unsigned long dummy );
static void fd_rwsec_done( int status );
static void fd_rwsec_done1(int status);
static void fd_writetrack( void );
static void fd_writetrack_done( int status );
static void fd_times_out( unsigned long dummy );
static void finish_fdc( void );
static void finish_fdc_done( int dummy );
static void setup_req_params( int drive );
static void redo_fd_request( void);
static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int
                     cmd, unsigned long param);
static void fd_probe( int drive );
static int fd_test_drive_present( int drive );
static void config_types( void );
static int floppy_open(struct block_device *bdev, fmode_t mode);
static void floppy_release(struct gendisk *disk, fmode_t mode);

/************************* End of Prototypes **************************/

static DEFINE_TIMER(motor_off_timer, fd_motor_off_timer, 0, 0);
static DEFINE_TIMER(readtrack_timer, fd_readtrack_check, 0, 0);
static DEFINE_TIMER(timeout_timer, fd_times_out, 0, 0);
static DEFINE_TIMER(fd_timer, check_change, 0, 0);

static void fd_end_request_cur(int err)
{
	if (!__blk_end_request_cur(fd_request, err))
		fd_request = NULL;
}

static inline void start_motor_off_timer(void)
{
	mod_timer(&motor_off_timer, jiffies + FD_MOTOR_OFF_DELAY);
	MotorOffTrys = 0;
}

static inline void start_check_change_timer( void )
{
	mod_timer(&fd_timer, jiffies + CHECK_CHANGE_DELAY);
}

static inline void start_timeout(void)
{
	mod_timer(&timeout_timer, jiffies + FLOPPY_TIMEOUT);
}

static inline void stop_timeout(void)
{
	del_timer(&timeout_timer);
}

/* Select the side to use. */

static void fd_select_side( int side )
{
	unsigned long flags;

	/* protect against various other ints mucking around with the PSG */
	local_irq_save(flags);

	sound_ym.rd_data_reg_sel = 14; /* Select PSG Port A */
	sound_ym.wd_data = (side == 0) ? sound_ym.rd_data_reg_sel | 0x01 :
	                                 sound_ym.rd_data_reg_sel & 0xfe;

	local_irq_restore(flags);
}

/* Select a drive, update the FDC's track register and set the correct
 * clock speed for this disk's type.
 */

static void fd_select_drive( int drive )
{
	unsigned long flags;
	unsigned char tmp;

	if (drive == SelectedDrive)
	  return;

	/* protect against various other ints mucking around with the PSG */
	local_irq_save(flags);
	sound_ym.rd_data_reg_sel = 14; /* Select PSG Port A */
	tmp = sound_ym.rd_data_reg_sel;
	sound_ym.wd_data = (tmp | DSKDRVNONE) & ~(drive == 0 ? DSKDRV0 : DSKDRV1);
	atari_dont_touch_floppy_select = 1;
	local_irq_restore(flags);

	/* restore track register to saved value */
	FDC_WRITE( FDCREG_TRACK, UD.track );
	udelay(25);

	/* select 8/16 MHz */
	if (UDT)
		if (ATARIHW_PRESENT(FDCSPEED))
			dma_wd.fdc_speed = UDT->fdc_speed;

	SelectedDrive = drive;
}

/* Deselect both drives. */

static void fd_deselect( void )
{
	unsigned long flags;

	/* protect against various other ints mucking around with the PSG */
	local_irq_save(flags);
	atari_dont_touch_floppy_select = 0;
	sound_ym.rd_data_reg_sel=14;	/* Select PSG Port A */
	sound_ym.wd_data = (sound_ym.rd_data_reg_sel |
			    (MACH_IS_FALCON ? 3 : 7)); /* no drives selected */
	/* On Falcon, the drive B select line is used on the printer port, so
	 * leave it alone... */
	SelectedDrive = -1;
	local_irq_restore(flags);
}

/* This timer function deselects the drives when the FDC switched the
 * motor off. The deselection cannot happen earlier because the FDC
 * counts the index signals, which arrive only if one drive is selected.
 */

static void fd_motor_off_timer( unsigned long dummy )
{
	unsigned char status;

	if (SelectedDrive < 0)
		/* no drive selected, needn't deselect anyone */
		return;

	if (stdma_islocked())
		goto retry;

	status = FDC_READ( FDCREG_STATUS );

	if (!(status & 0x80)) {
		/* motor already turned off by FDC -> deselect drives */
		MotorOn = 0;
		fd_deselect();
		return;
	}
	/* not yet off, try again */

  retry:
	/* Test again later; if tested too often, it seems there is no disk
	 * in the drive and the FDC will leave the motor on forever (or,
	 * at least until a disk is inserted). So we'll test only twice
	 * per second from then on...
	 */
	mod_timer(&motor_off_timer,
		  jiffies + (MotorOffTrys++ < FD_MOTOR_OFF_MAXTRY ? HZ/20 : HZ/2));
}

/* This function is repeatedly called to detect disk changes (as good
 * as possible) and keep track of the current state of the write protection.
 */

static void check_change( unsigned long dummy )
{
	static int    drive = 0;

	unsigned long flags;
	unsigned char old_porta;
	int			  stat;

	if (++drive > 1 || !UD.connected)
		drive = 0;

	/* protect against various other ints mucking around with the PSG */
	local_irq_save(flags);

	if (!stdma_islocked()) {
		sound_ym.rd_data_reg_sel = 14;
		old_porta = sound_ym.rd_data_reg_sel;
		sound_ym.wd_data = (old_porta | DSKDRVNONE) &
			               ~(drive == 0 ? DSKDRV0 : DSKDRV1);
		stat = !!(FDC_READ( FDCREG_STATUS ) & FDCSTAT_WPROT);
		sound_ym.wd_data = old_porta;

		if (stat != UD.wpstat) {
			DPRINT(( "wpstat[%d] = %d\n", drive, stat ));
			UD.wpstat = stat;
			set_bit (drive, &changed_floppies);
		}
	}
	local_irq_restore(flags);

	start_check_change_timer();
}

/* Handling of the Head Settling Flag: This flag should be set after each
 * seek operation, because we don't use seeks with verify.
 */

static inline void set_head_settle_flag(void)
{
	HeadSettleFlag = FDCCMDADD_E;
}

static inline int get_head_settle_flag(void)
{
	int	tmp = HeadSettleFlag;
	HeadSettleFlag = 0;
	return( tmp );
}

static inline void copy_buffer(void *from, void *to)
{
	ulong *p1 = (ulong *)from, *p2 = (ulong *)to;
	int cnt;

	for (cnt = 512/4; cnt; cnt--)
		*p2++ = *p1++;
}

/* General Interrupt Handling */

static void (*FloppyIRQHandler)( int status ) = NULL;

static irqreturn_t floppy_irq (int irq, void *dummy)
{
	unsigned char status;
	void (*handler)( int );

	handler = xchg(&FloppyIRQHandler, NULL);

	if (handler) {
		nop();
		status = FDC_READ( FDCREG_STATUS );
		DPRINT(("FDC irq, status = %02x handler = %08lx\n",status,(unsigned long)handler));
		handler( status );
	}
	else {
		DPRINT(("FDC irq, no handler\n"));
	}
	return IRQ_HANDLED;
}

/* Error handling: If some error happened, retry some times, then
 * recalibrate, then try again, and fail after MAX_ERRORS.
 */

static void fd_error( void )
{
	if (IsFormatting) {
		IsFormatting = 0;
		FormatError = 1;
		complete(&format_wait);
		return;
	}

	if (!fd_request)
		return;

	fd_request->errors++;
	if (fd_request->errors >= MAX_ERRORS) {
		printk(KERN_ERR "fd%d: too many errors.\n", SelectedDrive );
		fd_end_request_cur(-EIO);
	}
	else if (fd_request->errors == RECALIBRATE_ERRORS) {
		printk(KERN_WARNING "fd%d: recalibrating\n", SelectedDrive );
		if (SelectedDrive != -1)
			SUD.track = -1;
	}
	redo_fd_request();
}

#define	SET_IRQ_HANDLER(proc) do { FloppyIRQHandler = (proc); } while(0)

/* ---------- Formatting ---------- */

#define FILL(n,val)		\
    do {			\
	memset( p, val, n );	\
	p += n;			\
    } while(0)

static int do_format(int drive, int type, struct atari_format_descr *desc)
{
	unsigned char	*p;
	int sect, nsect;
	unsigned long	flags;

	DPRINT(("do_format( dr=%d tr=%d he=%d offs=%d )\n",
		drive, desc->track, desc->head, desc->sect_offset ));

	wait_event(fdc_wait, cmpxchg(&fdc_busy, 0, 1) == 0);
	local_irq_save(flags);
	stdma_lock(floppy_irq, NULL);
	atari_turnon_irq( IRQ_MFP_FDC ); /* should be already, just to be sure */
	local_irq_restore(flags);

	if (type) {
		if (--type >= NUM_DISK_MINORS ||
		    minor2disktype[type].drive_types > DriveType) {
			redo_fd_request();
			return -EINVAL;
		}
		type = minor2disktype[type].index;
		UDT = &atari_disk_type[type];
	}

	if (!UDT || desc->track >= UDT->blocks/UDT->spt/2 || desc->head >= 2) {
		redo_fd_request();
		return -EINVAL;
	}

	nsect = UDT->spt;
	p = TrackBuffer;
	/* The track buffer is used for the raw track data, so its
	   contents become invalid! */
	BufferDrive = -1;
	/* stop deselect timer */
	del_timer( &motor_off_timer );

	FILL( 60 * (nsect / 9), 0x4e );
	for( sect = 0; sect < nsect; ++sect ) {
		FILL( 12, 0 );
		FILL( 3, 0xf5 );
		*p++ = 0xfe;
		*p++ = desc->track;
		*p++ = desc->head;
		*p++ = (nsect + sect - desc->sect_offset) % nsect + 1;
		*p++ = 2;
		*p++ = 0xf7;
		FILL( 22, 0x4e );
		FILL( 12, 0 );
		FILL( 3, 0xf5 );
		*p++ = 0xfb;
		FILL( 512, 0xe5 );
		*p++ = 0xf7;
		FILL( 40, 0x4e );
	}
	FILL( TrackBuffer+BUFFER_SIZE-p, 0x4e );

	IsFormatting = 1;
	FormatError = 0;
	ReqTrack = desc->track;
	ReqSide  = desc->head;
	do_fd_action( drive );

	wait_for_completion(&format_wait);

	redo_fd_request();
	return( FormatError ? -EIO : 0 );
}

/* do_fd_action() is the general procedure for a fd request: All
 * required parameter settings (drive select, side select, track
 * position) are checked and set if needed. For each of these
 * parameters and the actual reading or writing exist two functions:
 * one that starts the setting (or skips it if possible) and one
 * callback for the "done" interrupt. Each done func calls the next
 * set function to propagate the request down to fd_rwsec_done().
 */

static void do_fd_action( int drive )
{
	DPRINT(("do_fd_action\n"));

	if (UseTrackbuffer && !IsFormatting) {
	repeat:
	    if (IS_BUFFERED( drive, ReqSide, ReqTrack )) {
		if (ReqCmd == READ) {
		    copy_buffer( SECTOR_BUFFER(ReqSector), ReqData );
		    if (++ReqCnt < blk_rq_cur_sectors(fd_request)) {
			/* read next sector */
			setup_req_params( drive );
			goto repeat;
		    }
		    else {
			/* all sectors finished */
			fd_end_request_cur(0);
			redo_fd_request();
			return;
		    }
		}
		else {
		    /* cmd == WRITE, pay attention to track buffer
		     * consistency! */
		    copy_buffer( ReqData, SECTOR_BUFFER(ReqSector) );
		}
	    }
	}

	if (SelectedDrive != drive)
		fd_select_drive( drive );

	if (UD.track == -1)
		fd_calibrate();
	else if (UD.track != ReqTrack << UDT->stretch)
		fd_seek();
	else if (IsFormatting)
		fd_writetrack();
	else
		fd_rwsec();
}

/* Seek to track 0 if the current track is unknown */

static void fd_calibrate( void )
{
	if (SUD.track >= 0) {
		fd_calibrate_done( 0 );
		return;
	}

	if (ATARIHW_PRESENT(FDCSPEED))
		dma_wd.fdc_speed = 0; 	/* always seek with 8 Mhz */;
	DPRINT(("fd_calibrate\n"));
	SET_IRQ_HANDLER( fd_calibrate_done );
	/* we can't verify, since the speed may be incorrect */
	FDC_WRITE( FDCREG_CMD, FDCCMD_RESTORE | SUD.steprate );

	NeedSeek = 1;
	MotorOn = 1;
	start_timeout();
	/* wait for IRQ */
}

static void fd_calibrate_done( int status )
{
	DPRINT(("fd_calibrate_done()\n"));
	stop_timeout();

	/* set the correct speed now */
	if (ATARIHW_PRESENT(FDCSPEED))
		dma_wd.fdc_speed = SUDT->fdc_speed;
	if (status & FDCSTAT_RECNF) {
		printk(KERN_ERR "fd%d: restore failed\n", SelectedDrive );
		fd_error();
	}
	else {
		SUD.track = 0;
		fd_seek();
	}
}

/* Seek the drive to the requested track. The drive must have been
 * calibrated at some point before this.
 */

static void fd_seek( void )
{
	if (SUD.track == ReqTrack << SUDT->stretch) {
		fd_seek_done( 0 );
		return;
	}

	if (ATARIHW_PRESENT(FDCSPEED)) {
		dma_wd.fdc_speed = 0;	/* always seek witch 8 Mhz */
		MFPDELAY();
	}

	DPRINT(("fd_seek() to track %d\n",ReqTrack));
	FDC_WRITE( FDCREG_DATA, ReqTrack << SUDT->stretch);
	udelay(25);
	SET_IRQ_HANDLER( fd_seek_done );
	FDC_WRITE( FDCREG_CMD, FDCCMD_SEEK | SUD.steprate );

	MotorOn = 1;
	set_head_settle_flag();
	start_timeout();
	/* wait for IRQ */
}

static void fd_seek_done( int status )
{
	DPRINT(("fd_seek_done()\n"));
	stop_timeout();

	/* set the correct speed */
	if (ATARIHW_PRESENT(FDCSPEED))
		dma_wd.fdc_speed = SUDT->fdc_speed;
	if (status & FDCSTAT_RECNF) {
		printk(KERN_ERR "fd%d: seek error (to track %d)\n",
				SelectedDrive, ReqTrack );
		/* we don't know exactly which track we are on now! */
		SUD.track = -1;
		fd_error();
	}
	else {
		SUD.track = ReqTrack << SUDT->stretch;
		NeedSeek = 0;
		if (IsFormatting)
			fd_writetrack();
		else
			fd_rwsec();
	}
}

/* This does the actual reading/writing after positioning the head
 * over the correct track.
 */

static int MultReadInProgress = 0;

static void fd_rwsec( void )
{
	unsigned long paddr, flags;
	unsigned int  rwflag, old_motoron;
	unsigned int track;

	DPRINT(("fd_rwsec(), Sec=%d, Access=%c\n",ReqSector, ReqCmd == WRITE ? 'w' : 'r' ));
	if (ReqCmd == WRITE) {
		if (ATARIHW_PRESENT(EXTD_DMA)) {
			paddr = virt_to_phys(ReqData);
		}
		else {
			copy_buffer( ReqData, DMABuffer );
			paddr = PhysDMABuffer;
		}
		dma_cache_maintenance( paddr, 512, 1 );
		rwflag = 0x100;
	}
	else {
		if (read_track)
			paddr = PhysTrackBuffer;
		else
			paddr = ATARIHW_PRESENT(EXTD_DMA) ?
				virt_to_phys(ReqData) : PhysDMABuffer;
		rwflag = 0;
	}

	fd_select_side( ReqSide );

	/* Start sector of this operation */
	FDC_WRITE( FDCREG_SECTOR, read_track ? 1 : ReqSector );
	MFPDELAY();
	/* Cheat for track if stretch != 0 */
	if (SUDT->stretch) {
		track = FDC_READ( FDCREG_TRACK);
		MFPDELAY();
		FDC_WRITE( FDCREG_TRACK, track >> SUDT->stretch);
	}
	udelay(25);

	/* Setup DMA */
	local_irq_save(flags);
	dma_wd.dma_lo = (unsigned char)paddr;
	MFPDELAY();
	paddr >>= 8;
	dma_wd.dma_md = (unsigned char)paddr;
	MFPDELAY();
	paddr >>= 8;
	if (ATARIHW_PRESENT(EXTD_DMA))
		st_dma_ext_dmahi = (unsigned short)paddr;
	else
		dma_wd.dma_hi = (unsigned char)paddr;
	MFPDELAY();
	local_irq_restore(flags);

	/* Clear FIFO and switch DMA to correct mode */
	dma_wd.dma_mode_status = 0x90 | rwflag;
	MFPDELAY();
	dma_wd.dma_mode_status = 0x90 | (rwflag ^ 0x100);
	MFPDELAY();
	dma_wd.dma_mode_status = 0x90 | rwflag;
	MFPDELAY();

	/* How many sectors for DMA */
	dma_wd.fdc_acces_seccount = read_track ? SUDT->spt : 1;

	udelay(25);

	/* Start operation */
	dma_wd.dma_mode_status = FDCSELREG_STP | rwflag;
	udelay(25);
	SET_IRQ_HANDLER( fd_rwsec_done );
	dma_wd.fdc_acces_seccount =
	  (get_head_settle_flag() |
	   (rwflag ? FDCCMD_WRSEC : (FDCCMD_RDSEC | (read_track ? FDCCMDADD_M : 0))));

	old_motoron = MotorOn;
	MotorOn = 1;
	NeedSeek = 1;
	/* wait for interrupt */

	if (read_track) {
		/* If reading a whole track, wait about one disk rotation and
		 * then check if all sectors are read. The FDC will even
		 * search for the first non-existent sector and need 1 sec to
		 * recognise that it isn't present :-(
		 */
		MultReadInProgress = 1;
		mod_timer(&readtrack_timer,
			  /* 1 rot. + 5 rot.s if motor was off  */
			  jiffies + HZ/5 + (old_motoron ? 0 : HZ));
	}
	start_timeout();
}

static void fd_readtrack_check( unsigned long dummy )
{
	unsigned long flags, addr, addr2;

	local_irq_save(flags);

	if (!MultReadInProgress) {
		/* This prevents a race condition that could arise if the
		 * interrupt is triggered while the calling of this timer
		 * callback function takes place. The IRQ function then has
		 * already cleared 'MultReadInProgress'  when flow of control
		 * gets here.
		 */
		local_irq_restore(flags);
		return;
	}

	/* get the current DMA address */
	/* ++ f.a. read twice to avoid being fooled by switcher */
	addr = 0;
	do {
		addr2 = addr;
		addr = dma_wd.dma_lo & 0xff;
		MFPDELAY();
		addr |= (dma_wd.dma_md & 0xff) << 8;
		MFPDELAY();
		if (ATARIHW_PRESENT( EXTD_DMA ))
			addr |= (st_dma_ext_dmahi & 0xffff) << 16;
		else
			addr |= (dma_wd.dma_hi & 0xff) << 16;
		MFPDELAY();
	} while(addr != addr2);

	if (addr >= PhysTrackBuffer + SUDT->spt*512) {
		/* already read enough data, force an FDC interrupt to stop
		 * the read operation
		 */
		SET_IRQ_HANDLER( NULL );
		MultReadInProgress = 0;
		local_irq_restore(flags);
		DPRINT(("fd_readtrack_check(): done\n"));
		FDC_WRITE( FDCREG_CMD, FDCCMD_FORCI );
		udelay(25);

		/* No error until now -- the FDC would have interrupted
		 * otherwise!
		 */
		fd_rwsec_done1(0);
	}
	else {
		/* not yet finished, wait another tenth rotation */
		local_irq_restore(flags);
		DPRINT(("fd_readtrack_check(): not yet finished\n"));
		mod_timer(&readtrack_timer, jiffies + HZ/5/10);
	}
}

static void fd_rwsec_done( int status )
{
	DPRINT(("fd_rwsec_done()\n"));

	if (read_track) {
		del_timer(&readtrack_timer);
		if (!MultReadInProgress)
			return;
		MultReadInProgress = 0;
	}
	fd_rwsec_done1(status);
}

static void fd_rwsec_done1(int status)
{
	unsigned int track;

	stop_timeout();

	/* Correct the track if stretch != 0 */
	if (SUDT->stretch) {
		track = FDC_READ( FDCREG_TRACK);
		MFPDELAY();
		FDC_WRITE( FDCREG_TRACK, track << SUDT->stretch);
	}

	if (!UseTrackbuffer) {
		dma_wd.dma_mode_status = 0x90;
		MFPDELAY();
		if (!(dma_wd.dma_mode_status & 0x01)) {
			printk(KERN_ERR "fd%d: DMA error\n", SelectedDrive );
			goto err_end;
		}
	}
	MFPDELAY();

	if (ReqCmd == WRITE && (status & FDCSTAT_WPROT)) {
		printk(KERN_NOTICE "fd%d: is write protected\n", SelectedDrive );
		goto err_end;
	}
	if ((status & FDCSTAT_RECNF) &&
	    /* RECNF is no error after a multiple read when the FDC
	       searched for a non-existent sector! */
	    !(read_track && FDC_READ(FDCREG_SECTOR) > SUDT->spt)) {
		if (Probing) {
			if (SUDT > atari_disk_type) {
			    if (SUDT[-1].blocks > ReqBlock) {
				/* try another disk type */
				SUDT--;
				set_capacity(unit[SelectedDrive].disk,
							SUDT->blocks);
			    } else
				Probing = 0;
			}
			else {
				if (SUD.flags & FTD_MSG)
					printk(KERN_INFO "fd%d: Auto-detected floppy type %s\n",
					       SelectedDrive, SUDT->name );
				Probing=0;
			}
		} else {
/* record not found, but not probing. Maybe stretch wrong ? Restart probing */
			if (SUD.autoprobe) {
				SUDT = atari_disk_type + StartDiskType[DriveType];
				set_capacity(unit[SelectedDrive].disk,
							SUDT->blocks);
				Probing = 1;
			}
		}
		if (Probing) {
			if (ATARIHW_PRESENT(FDCSPEED)) {
				dma_wd.fdc_speed = SUDT->fdc_speed;
				MFPDELAY();
			}
			setup_req_params( SelectedDrive );
			BufferDrive = -1;
			do_fd_action( SelectedDrive );
			return;
		}

		printk(KERN_ERR "fd%d: sector %d not found (side %d, track %d)\n",
		       SelectedDrive, FDC_READ (FDCREG_SECTOR), ReqSide, ReqTrack );
		goto err_end;
	}
	if (status & FDCSTAT_CRC) {
		printk(KERN_ERR "fd%d: CRC error (side %d, track %d, sector %d)\n",
		       SelectedDrive, ReqSide, ReqTrack, FDC_READ (FDCREG_SECTOR) );
		goto err_end;
	}
	if (status & FDCSTAT_LOST) {
		printk(KERN_ERR "fd%d: lost data (side %d, track %d, sector %d)\n",
		       SelectedDrive, ReqSide, ReqTrack, FDC_READ (FDCREG_SECTOR) );
		goto err_end;
	}

	Probing = 0;

	if (ReqCmd == READ) {
		if (!read_track) {
			void *addr;
			addr = ATARIHW_PRESENT( EXTD_DMA ) ? ReqData : DMABuffer;
			dma_cache_maintenance( virt_to_phys(addr), 512, 0 );
			if (!ATARIHW_PRESENT( EXTD_DMA ))
				copy_buffer (addr, ReqData);
		} else {
			dma_cache_maintenance( PhysTrackBuffer, MaxSectors[DriveType] * 512, 0 );
			BufferDrive = SelectedDrive;
			BufferSide  = ReqSide;
			BufferTrack = ReqTrack;
			copy_buffer (SECTOR_BUFFER (ReqSector), ReqData);
		}
	}

	if (++ReqCnt < blk_rq_cur_sectors(fd_request)) {
		/* read next sector */
		setup_req_params( SelectedDrive );
		do_fd_action( SelectedDrive );
	}
	else {
		/* all sectors finished */
		fd_end_request_cur(0);
		redo_fd_request();
	}
	return;

  err_end:
	BufferDrive = -1;
	fd_error();
}

static void fd_writetrack( void )
{
	unsigned long paddr, flags;
	unsigned int track;

	DPRINT(("fd_writetrack() Tr=%d Si=%d\n", ReqTrack, ReqSide ));

	paddr = PhysTrackBuffer;
	dma_cache_maintenance( paddr, BUFFER_SIZE, 1 );

	fd_select_side( ReqSide );

	/* Cheat for track if stretch != 0 */
	if (SUDT->stretch) {
		track = FDC_READ( FDCREG_TRACK);
		MFPDELAY();
		FDC_WRITE(FDCREG_TRACK,track >> SUDT->stretch);
	}
	udelay(40);

	/* Setup DMA */
	local_irq_save(flags);
	dma_wd.dma_lo = (unsigned char)paddr;
	MFPDELAY();
	paddr >>= 8;
	dma_wd.dma_md = (unsigned char)paddr;
	MFPDELAY();
	paddr >>= 8;
	if (ATARIHW_PRESENT( EXTD_DMA ))
		st_dma_ext_dmahi = (unsigned short)paddr;
	else
		dma_wd.dma_hi = (unsigned char)paddr;
	MFPDELAY();
	local_irq_restore(flags);

	/* Clear FIFO and switch DMA to correct mode */
	dma_wd.dma_mode_status = 0x190;
	MFPDELAY();
	dma_wd.dma_mode_status = 0x90;
	MFPDELAY();
	dma_wd.dma_mode_status = 0x190;
	MFPDELAY();

	/* How many sectors for DMA */
	dma_wd.fdc_acces_seccount = BUFFER_SIZE/512;
	udelay(40);

	/* Start operation */
	dma_wd.dma_mode_status = FDCSELREG_STP | 0x100;
	udelay(40);
	SET_IRQ_HANDLER( fd_writetrack_done );
	dma_wd.fdc_acces_seccount = FDCCMD_WRTRA | get_head_settle_flag();

	MotorOn = 1;
	start_timeout();
	/* wait for interrupt */
}

static void fd_writetrack_done( int status )
{
	DPRINT(("fd_writetrack_done()\n"));

	stop_timeout();

	if (status & FDCSTAT_WPROT) {
		printk(KERN_NOTICE "fd%d: is write protected\n", SelectedDrive );
		goto err_end;
	}
	if (status & FDCSTAT_LOST) {
		printk(KERN_ERR "fd%d: lost data (side %d, track %d)\n",
				SelectedDrive, ReqSide, ReqTrack );
		goto err_end;
	}

	complete(&format_wait);
	return;

  err_end:
	fd_error();
}

static void fd_times_out( unsigned long dummy )
{
	atari_disable_irq( IRQ_MFP_FDC );
	if (!FloppyIRQHandler) goto end; /* int occurred after timer was fired, but
					  * before we came here... */

	SET_IRQ_HANDLER( NULL );
	/* If the timeout occurred while the readtrack_check timer was
	 * active, we need to cancel it, else bad things will happen */
	if (UseTrackbuffer)
		del_timer( &readtrack_timer );
	FDC_WRITE( FDCREG_CMD, FDCCMD_FORCI );
	udelay( 25 );

	printk(KERN_ERR "floppy timeout\n" );
	fd_error();
  end:
	atari_enable_irq( IRQ_MFP_FDC );
}

/* The (noop) seek operation here is needed to make the WP bit in the
 * FDC status register accessible for check_change. If the last disk
 * operation would have been a RDSEC, this bit would always read as 0
 * no matter what :-( To save time, the seek goes to the track we're
 * already on.
 */

static void finish_fdc( void )
{
	if (!NeedSeek) {
		finish_fdc_done( 0 );
	}
	else {
		DPRINT(("finish_fdc: dummy seek started\n"));
		FDC_WRITE (FDCREG_DATA, SUD.track);
		SET_IRQ_HANDLER( finish_fdc_done );
		FDC_WRITE (FDCREG_CMD, FDCCMD_SEEK);
		MotorOn = 1;
		start_timeout();
		/* we must wait for the IRQ here, because the ST-DMA
		   is released immediately afterwards and the interrupt
		   may be delivered to the wrong driver. */
	  }
}

static void finish_fdc_done( int dummy )
{
	unsigned long flags;

	DPRINT(("finish_fdc_done entered\n"));
	stop_timeout();
	NeedSeek = 0;

	if (timer_pending(&fd_timer) && time_before(fd_timer.expires, jiffies + 5))
		/* If the check for a disk change is done too early after this
		 * last seek command, the WP bit still reads wrong :-((
		 */
		mod_timer(&fd_timer, jiffies + 5);
	else
		start_check_change_timer();
	start_motor_off_timer();

	local_irq_save(flags);
	stdma_release();
	fdc_busy = 0;
	wake_up( &fdc_wait );
	local_irq_restore(flags);

	DPRINT(("finish_fdc() finished\n"));
}

/* The detection of disk changes is a dark chapter in Atari history :-(
 * Because the "Drive ready" signal isn't present in the Atari
 * hardware, one has to rely on the "Write Protect". This works fine,
 * as long as no write protected disks are used. TOS solves this
 * problem by introducing tri-state logic ("maybe changed") and
 * looking at the serial number in block 0. This isn't possible for
 * Linux, since the floppy driver can't make assumptions about the
 * filesystem used on the disk and thus the contents of block 0. I've
 * chosen the method to always say "The disk was changed" if it is
 * unsure whether it was. This implies that every open or mount
 * invalidates the disk buffers if you work with write protected
 * disks. But at least this is better than working with incorrect data
 * due to unrecognised disk changes.
 */

static unsigned int floppy_check_events(struct gendisk *disk,
					unsigned int clearing)
{
	struct atari_floppy_struct *p = disk->private_data;
	unsigned int drive = p - unit;
	if (test_bit (drive, &fake_change)) {
		/* simulated change (e.g. after formatting) */
		return DISK_EVENT_MEDIA_CHANGE;
	}
	if (test_bit (drive, &changed_floppies)) {
		/* surely changed (the WP signal changed at least once) */
		return DISK_EVENT_MEDIA_CHANGE;
	}
	if (UD.wpstat) {
		/* WP is on -> could be changed: to be sure, buffers should be
		 * invalidated...
		 */
		return DISK_EVENT_MEDIA_CHANGE;
	}

	return 0;
}

static int floppy_revalidate(struct gendisk *disk)
{
	struct atari_floppy_struct *p = disk->private_data;
	unsigned int drive = p - unit;

	if (test_bit(drive, &changed_floppies) ||
	    test_bit(drive, &fake_change) ||
	    p->disktype == 0) {
		if (UD.flags & FTD_MSG)
			printk(KERN_ERR "floppy: clear format %p!\n", UDT);
		BufferDrive = -1;
		clear_bit(drive, &fake_change);
		clear_bit(drive, &changed_floppies);
		/* MSch: clearing geometry makes sense only for autoprobe
		   formats, for 'permanent user-defined' parameter:
		   restore default_params[] here if flagged valid! */
		if (default_params[drive].blocks == 0)
			UDT = NULL;
		else
			UDT = &default_params[drive];
	}
	return 0;
}

/* This sets up the global variables describing the current request. */

static void setup_req_params( int drive )
{
	int block = ReqBlock + ReqCnt;

	ReqTrack = block / UDT->spt;
	ReqSector = block - ReqTrack * UDT->spt + 1;
	ReqSide = ReqTrack & 1;
	ReqTrack >>= 1;
	ReqData = ReqBuffer + 512 * ReqCnt;

	if (UseTrackbuffer)
		read_track = (ReqCmd == READ && fd_request->errors == 0);
	else
		read_track = 0;

	DPRINT(("Request params: Si=%d Tr=%d Se=%d Data=%08lx\n",ReqSide,
			ReqTrack, ReqSector, (unsigned long)ReqData ));
}

/*
 * Round-robin between our available drives, doing one request from each
 */
static struct request *set_next_request(void)
{
	struct request_queue *q;
	int old_pos = fdc_queue;
	struct request *rq = NULL;

	do {
		q = unit[fdc_queue].disk->queue;
		if (++fdc_queue == FD_MAX_UNITS)
			fdc_queue = 0;
		if (q) {
			rq = blk_fetch_request(q);
			if (rq)
				break;
		}
	} while (fdc_queue != old_pos);

	return rq;
}

static void redo_fd_request(void)
{
	int drive, type;
	struct atari_floppy_struct *floppy;

	DPRINT(("redo_fd_request: fd_request=%p dev=%s fd_request->sector=%ld\n",
		fd_request, fd_request ? fd_request->rq_disk->disk_name : "",
		fd_request ? blk_rq_pos(fd_request) : 0 ));

	IsFormatting = 0;

repeat:
	if (!fd_request) {
		fd_request = set_next_request();
		if (!fd_request)
			goto the_end;
	}

	floppy = fd_request->rq_disk->private_data;
	drive = floppy - unit;
	type = floppy->type;

	if (!UD.connected) {
		/* drive not connected */
		printk(KERN_ERR "Unknown Device: fd%d\n", drive );
		fd_end_request_cur(-EIO);
		goto repeat;
	}

	if (type == 0) {
		if (!UDT) {
			Probing = 1;
			UDT = atari_disk_type + StartDiskType[DriveType];
			set_capacity(floppy->disk, UDT->blocks);
			UD.autoprobe = 1;
		}
	}
	else {
		/* user supplied disk type */
		if (--type >= NUM_DISK_MINORS) {
			printk(KERN_WARNING "fd%d: invalid disk format", drive );
			fd_end_request_cur(-EIO);
			goto repeat;
		}
		if (minor2disktype[type].drive_types > DriveType)  {
			printk(KERN_WARNING "fd%d: unsupported disk format", drive );
			fd_end_request_cur(-EIO);
			goto repeat;
		}
		type = minor2disktype[type].index;
		UDT = &atari_disk_type[type];
		set_capacity(floppy->disk, UDT->blocks);
		UD.autoprobe = 0;
	}

	if (blk_rq_pos(fd_request) + 1 > UDT->blocks) {
		fd_end_request_cur(-EIO);
		goto repeat;
	}

	/* stop deselect timer */
	del_timer( &motor_off_timer );

	ReqCnt = 0;
	ReqCmd = rq_data_dir(fd_request);
	ReqBlock = blk_rq_pos(fd_request);
	ReqBuffer = bio_data(fd_request->bio);
	setup_req_params( drive );
	do_fd_action( drive );

	return;

  the_end:
	finish_fdc();
}

void do_fd_request(struct request_queue * q)
{
	DPRINT(("do_fd_request for pid %d\n",current->pid));
	wait_event(fdc_wait, cmpxchg(&fdc_busy, 0, 1) == 0);
	stdma_lock(floppy_irq, NULL);

	atari_disable_irq( IRQ_MFP_FDC );
	redo_fd_request();
	atari_enable_irq( IRQ_MFP_FDC );
}

static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode,
		    unsigned int cmd, unsigned long param)
{
	struct gendisk *disk = bdev->bd_disk;
	struct atari_floppy_struct *floppy = disk->private_data;
	int drive = floppy - unit;
	int type = floppy->type;
	struct atari_format_descr fmt_desc;
	struct atari_disk_type *dtp;
	struct floppy_struct getprm;
	int settype;
	struct floppy_struct setprm;
	void __user *argp = (void __user *)param;

	switch (cmd) {
	case FDGETPRM:
		if (type) {
			if (--type >= NUM_DISK_MINORS)
				return -ENODEV;
			if (minor2disktype[type].drive_types > DriveType)
				return -ENODEV;
			type = minor2disktype[type].index;
			dtp = &atari_disk_type[type];
			if (UD.flags & FTD_MSG)
			    printk (KERN_ERR "floppy%d: found dtp %p name %s!\n",
			        drive, dtp, dtp->name);
		}
		else {
			if (!UDT)
				return -ENXIO;
			else
				dtp = UDT;
		}
		memset((void *)&getprm, 0, sizeof(getprm));
		getprm.size = dtp->blocks;
		getprm.sect = dtp->spt;
		getprm.head = 2;
		getprm.track = dtp->blocks/dtp->spt/2;
		getprm.stretch = dtp->stretch;
		if (copy_to_user(argp, &getprm, sizeof(getprm)))
			return -EFAULT;
		return 0;
	}
	switch (cmd) {
	case FDSETPRM:
	case FDDEFPRM:
	        /*
		 * MSch 7/96: simple 'set geometry' case: just set the
		 * 'default' device params (minor == 0).
		 * Currently, the drive geometry is cleared after each
		 * disk change and subsequent revalidate()! simple
		 * implementation of FDDEFPRM: save geometry from a
		 * FDDEFPRM call and restore it in floppy_revalidate() !
		 */

		/* get the parameters from user space */
		if (floppy->ref != 1 && floppy->ref != -1)
			return -EBUSY;
		if (copy_from_user(&setprm, argp, sizeof(setprm)))
			return -EFAULT;
		/*
		 * first of all: check for floppy change and revalidate,
		 * or the next access will revalidate - and clear UDT :-(
		 */

		if (floppy_check_events(disk, 0))
		        floppy_revalidate(disk);

		if (UD.flags & FTD_MSG)
		    printk (KERN_INFO "floppy%d: setting size %d spt %d str %d!\n",
			drive, setprm.size, setprm.sect, setprm.stretch);

		/* what if type > 0 here? Overwrite specified entry ? */
		if (type) {
		        /* refuse to re-set a predefined type for now */
			redo_fd_request();
			return -EINVAL;
		}

		/*
		 * type == 0: first look for a matching entry in the type list,
		 * and set the UD.disktype field to use the perdefined entry.
		 * TODO: add user-defined format to head of autoprobe list ?
		 * Useful to include the user-type for future autodetection!
		 */

		for (settype = 0; settype < NUM_DISK_MINORS; settype++) {
			int setidx = 0;
			if (minor2disktype[settype].drive_types > DriveType) {
				/* skip this one, invalid for drive ... */
				continue;
			}
			setidx = minor2disktype[settype].index;
			dtp = &atari_disk_type[setidx];

			/* found matching entry ?? */
			if (   dtp->blocks  == setprm.size
			    && dtp->spt     == setprm.sect
			    && dtp->stretch == setprm.stretch ) {
				if (UD.flags & FTD_MSG)
				    printk (KERN_INFO "floppy%d: setting %s %p!\n",
				        drive, dtp->name, dtp);
				UDT = dtp;
				set_capacity(floppy->disk, UDT->blocks);

				if (cmd == FDDEFPRM) {
				  /* save settings as permanent default type */
				  default_params[drive].name    = dtp->name;
				  default_params[drive].spt     = dtp->spt;
				  default_params[drive].blocks  = dtp->blocks;
				  default_params[drive].fdc_speed = dtp->fdc_speed;
				  default_params[drive].stretch = dtp->stretch;
				}

				return 0;
			}

		}

		/* no matching disk type found above - setting user_params */

	       	if (cmd == FDDEFPRM) {
			/* set permanent type */
			dtp = &default_params[drive];
		} else
			/* set user type (reset by disk change!) */
			dtp = &user_params[drive];

		dtp->name   = "user format";
		dtp->blocks = setprm.size;
		dtp->spt    = setprm.sect;
		if (setprm.sect > 14)
			dtp->fdc_speed = 3;
		else
			dtp->fdc_speed = 0;
		dtp->stretch = setprm.stretch;

		if (UD.flags & FTD_MSG)
			printk (KERN_INFO "floppy%d: blk %d spt %d str %d!\n",
				drive, dtp->blocks, dtp->spt, dtp->stretch);

		/* sanity check */
		if (setprm.track != dtp->blocks/dtp->spt/2 ||
		    setprm.head != 2) {
			redo_fd_request();
			return -EINVAL;
		}

		UDT = dtp;
		set_capacity(floppy->disk, UDT->blocks);

		return 0;
	case FDMSGON:
		UD.flags |= FTD_MSG;
		return 0;
	case FDMSGOFF:
		UD.flags &= ~FTD_MSG;
		return 0;
	case FDSETEMSGTRESH:
		return -EINVAL;
	case FDFMTBEG:
		return 0;
	case FDFMTTRK:
		if (floppy->ref != 1 && floppy->ref != -1)
			return -EBUSY;
		if (copy_from_user(&fmt_desc, argp, sizeof(fmt_desc)))
			return -EFAULT;
		return do_format(drive, type, &fmt_desc);
	case FDCLRPRM:
		UDT = NULL;
		/* MSch: invalidate default_params */
		default_params[drive].blocks  = 0;
		set_capacity(floppy->disk, MAX_DISK_SIZE * 2);
	case FDFMTEND:
	case FDFLUSH:
		/* invalidate the buffer track to force a reread */
		BufferDrive = -1;
		set_bit(drive, &fake_change);
		check_disk_change(bdev);
		return 0;
	default:
		return -EINVAL;
	}
}

static int fd_ioctl(struct block_device *bdev, fmode_t mode,
			     unsigned int cmd, unsigned long arg)
{
	int ret;

	mutex_lock(&ataflop_mutex);
	ret = fd_locked_ioctl(bdev, mode, cmd, arg);
	mutex_unlock(&ataflop_mutex);

	return ret;
}

/* Initialize the 'unit' variable for drive 'drive' */

static void __init fd_probe( int drive )
{
	UD.connected = 0;
	UDT  = NULL;

	if (!fd_test_drive_present( drive ))
		return;

	UD.connected = 1;
	UD.track     = 0;
	switch( UserSteprate[drive] ) {
	case 2:
		UD.steprate = FDCSTEP_2;
		break;
	case 3:
		UD.steprate = FDCSTEP_3;
		break;
	case 6:
		UD.steprate = FDCSTEP_6;
		break;
	case 12:
		UD.steprate = FDCSTEP_12;
		break;
	default: /* should be -1 for "not set by user" */
		if (ATARIHW_PRESENT( FDCSPEED ) || MACH_IS_MEDUSA)
			UD.steprate = FDCSTEP_3;
		else
			UD.steprate = FDCSTEP_6;
		break;
	}
	MotorOn = 1;	/* from probe restore operation! */
}

/* This function tests the physical presence of a floppy drive (not
 * whether a disk is inserted). This is done by issuing a restore
 * command, waiting max. 2 seconds (that should be enough to move the
 * head across the whole disk) and looking at the state of the "TR00"
 * signal. This should now be raised if there is a drive connected
 * (and there is no hardware failure :-) Otherwise, the drive is
 * declared absent.
 */

static int __init fd_test_drive_present( int drive )
{
	unsigned long timeout;
	unsigned char status;
	int ok;

	if (drive >= (MACH_IS_FALCON ? 1 : 2)) return( 0 );
	fd_select_drive( drive );

	/* disable interrupt temporarily */
	atari_turnoff_irq( IRQ_MFP_FDC );
	FDC_WRITE (FDCREG_TRACK, 0xff00);
	FDC_WRITE( FDCREG_CMD, FDCCMD_RESTORE | FDCCMDADD_H | FDCSTEP_6 );

	timeout = jiffies + 2*HZ+HZ/2;
	while (time_before(jiffies, timeout))
		if (!(st_mfp.par_dt_reg & 0x20))
			break;

	status = FDC_READ( FDCREG_STATUS );
	ok = (status & FDCSTAT_TR00) != 0;

	/* force interrupt to abort restore operation (FDC would try
	 * about 50 seconds!) */
	FDC_WRITE( FDCREG_CMD, FDCCMD_FORCI );
	udelay(500);
	status = FDC_READ( FDCREG_STATUS );
	udelay(20);

	if (ok) {
		/* dummy seek command to make WP bit accessible */
		FDC_WRITE( FDCREG_DATA, 0 );
		FDC_WRITE( FDCREG_CMD, FDCCMD_SEEK );
		while( st_mfp.par_dt_reg & 0x20 )
			;
		status = FDC_READ( FDCREG_STATUS );
	}

	atari_turnon_irq( IRQ_MFP_FDC );
	return( ok );
}

/* Look how many and which kind of drives are connected. If there are
 * floppies, additionally start the disk-change and motor-off timers.
 */

static void __init config_types( void )
{
	int drive, cnt = 0;

	/* for probing drives, set the FDC speed to 8 MHz */
	if (ATARIHW_PRESENT(FDCSPEED))
		dma_wd.fdc_speed = 0;

	printk(KERN_INFO "Probing floppy drive(s):\n");
	for( drive = 0; drive < FD_MAX_UNITS; drive++ ) {
		fd_probe( drive );
		if (UD.connected) {
			printk(KERN_INFO "fd%d\n", drive);
			++cnt;
		}
	}

	if (FDC_READ( FDCREG_STATUS ) & FDCSTAT_BUSY) {
		/* If FDC is still busy from probing, give it another FORCI
		 * command to abort the operation. If this isn't done, the FDC
		 * will interrupt later and its IRQ line stays low, because
		 * the status register isn't read. And this will block any
		 * interrupts on this IRQ line :-(
		 */
		FDC_WRITE( FDCREG_CMD, FDCCMD_FORCI );
		udelay(500);
		FDC_READ( FDCREG_STATUS );
		udelay(20);
	}

	if (cnt > 0) {
		start_motor_off_timer();
		if (cnt == 1) fd_select_drive( 0 );
		start_check_change_timer();
	}
}

/*
 * floppy_open check for aliasing (/dev/fd0 can be the same as
 * /dev/PS0 etc), and disallows simultaneous access to the same
 * drive with different device numbers.
 */

static int floppy_open(struct block_device *bdev, fmode_t mode)
{
	struct atari_floppy_struct *p = bdev->bd_disk->private_data;
	int type  = MINOR(bdev->bd_dev) >> 2;

	DPRINT(("fd_open: type=%d\n",type));
	if (p->ref && p->type != type)
		return -EBUSY;

	if (p->ref == -1 || (p->ref && mode & FMODE_EXCL))
		return -EBUSY;

	if (mode & FMODE_EXCL)
		p->ref = -1;
	else
		p->ref++;

	p->type = type;

	if (mode & FMODE_NDELAY)
		return 0;

	if (mode & (FMODE_READ|FMODE_WRITE)) {
		check_disk_change(bdev);
		if (mode & FMODE_WRITE) {
			if (p->wpstat) {
				if (p->ref < 0)
					p->ref = 0;
				else
					p->ref--;
				return -EROFS;
			}
		}
	}
	return 0;
}

static int floppy_unlocked_open(struct block_device *bdev, fmode_t mode)
{
	int ret;

	mutex_lock(&ataflop_mutex);
	ret = floppy_open(bdev, mode);
	mutex_unlock(&ataflop_mutex);

	return ret;
}

static void floppy_release(struct gendisk *disk, fmode_t mode)
{
	struct atari_floppy_struct *p = disk->private_data;
	mutex_lock(&ataflop_mutex);
	if (p->ref < 0)
		p->ref = 0;
	else if (!p->ref--) {
		printk(KERN_ERR "floppy_release with fd_ref == 0");
		p->ref = 0;
	}
	mutex_unlock(&ataflop_mutex);
}

static const struct block_device_operations floppy_fops = {
	.owner		= THIS_MODULE,
	.open		= floppy_unlocked_open,
	.release	= floppy_release,
	.ioctl		= fd_ioctl,
	.check_events	= floppy_check_events,
	.revalidate_disk= floppy_revalidate,
};

static struct kobject *floppy_find(dev_t dev, int *part, void *data)
{
	int drive = *part & 3;
	int type  = *part >> 2;
	if (drive >= FD_MAX_UNITS || type > NUM_DISK_MINORS)
		return NULL;
	*part = 0;
	return get_disk(unit[drive].disk);
}

static int __init atari_floppy_init (void)
{
	int i;

	if (!MACH_IS_ATARI)
		/* Amiga, Mac, ... don't have Atari-compatible floppy :-) */
		return -ENODEV;

	if (register_blkdev(FLOPPY_MAJOR,"fd"))
		return -EBUSY;

	for (i = 0; i < FD_MAX_UNITS; i++) {
		unit[i].disk = alloc_disk(1);
		if (!unit[i].disk)
			goto Enomem;
	}

	if (UseTrackbuffer < 0)
		/* not set by user -> use default: for now, we turn
		   track buffering off for all Medusas, though it
		   could be used with ones that have a counter
		   card. But the test is too hard :-( */
		UseTrackbuffer = !MACH_IS_MEDUSA;

	/* initialize variables */
	SelectedDrive = -1;
	BufferDrive = -1;

	DMABuffer = atari_stram_alloc(BUFFER_SIZE+512, "ataflop");
	if (!DMABuffer) {
		printk(KERN_ERR "atari_floppy_init: cannot get dma buffer\n");
		goto Enomem;
	}
	TrackBuffer = DMABuffer + 512;
	PhysDMABuffer = atari_stram_to_phys(DMABuffer);
	PhysTrackBuffer = virt_to_phys(TrackBuffer);
	BufferDrive = BufferSide = BufferTrack = -1;

	for (i = 0; i < FD_MAX_UNITS; i++) {
		unit[i].track = -1;
		unit[i].flags = 0;
		unit[i].disk->major = FLOPPY_MAJOR;
		unit[i].disk->first_minor = i;
		sprintf(unit[i].disk->disk_name, "fd%d", i);
		unit[i].disk->fops = &floppy_fops;
		unit[i].disk->private_data = &unit[i];
		unit[i].disk->queue = blk_init_queue(do_fd_request,
					&ataflop_lock);
		if (!unit[i].disk->queue)
			goto Enomem;
		set_capacity(unit[i].disk, MAX_DISK_SIZE * 2);
		add_disk(unit[i].disk);
	}

	blk_register_region(MKDEV(FLOPPY_MAJOR, 0), 256, THIS_MODULE,
				floppy_find, NULL, NULL);

	printk(KERN_INFO "Atari floppy driver: max. %cD, %strack buffering\n",
	       DriveType == 0 ? 'D' : DriveType == 1 ? 'H' : 'E',
	       UseTrackbuffer ? "" : "no ");
	config_types();

	return 0;
Enomem:
	while (i--) {
		struct request_queue *q = unit[i].disk->queue;

		put_disk(unit[i].disk);
		if (q)
			blk_cleanup_queue(q);
	}

	unregister_blkdev(FLOPPY_MAJOR, "fd");
	return -ENOMEM;
}

#ifndef MODULE
static int __init atari_floppy_setup(char *str)
{
	int ints[3 + FD_MAX_UNITS];
	int i;

	if (!MACH_IS_ATARI)
		return 0;

	str = get_options(str, 3 + FD_MAX_UNITS, ints);

	if (ints[0] < 1) {
		printk(KERN_ERR "ataflop_setup: no arguments!\n" );
		return 0;
	}
	else if (ints[0] > 2+FD_MAX_UNITS) {
		printk(KERN_ERR "ataflop_setup: too many arguments\n" );
	}

	if (ints[1] < 0 || ints[1] > 2)
		printk(KERN_ERR "ataflop_setup: bad drive type\n" );
	else
		DriveType = ints[1];

	if (ints[0] >= 2)
		UseTrackbuffer = (ints[2] > 0);

	for( i = 3; i <= ints[0] && i-3 < FD_MAX_UNITS; ++i ) {
		if (ints[i] != 2 && ints[i] != 3 && ints[i] != 6 && ints[i] != 12)
			printk(KERN_ERR "ataflop_setup: bad steprate\n" );
		else
			UserSteprate[i-3] = ints[i];
	}
	return 1;
}

__setup("floppy=", atari_floppy_setup);
#endif

static void __exit atari_floppy_exit(void)
{
	int i;
	blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256);
	for (i = 0; i < FD_MAX_UNITS; i++) {
		struct request_queue *q = unit[i].disk->queue;

		del_gendisk(unit[i].disk);
		put_disk(unit[i].disk);
		blk_cleanup_queue(q);
	}
	unregister_blkdev(FLOPPY_MAJOR, "fd");

	del_timer_sync(&fd_timer);
	atari_stram_free( DMABuffer );
}

module_init(atari_floppy_init)
module_exit(atari_floppy_exit)

MODULE_LICENSE("GPL");

redpajama_set_name: RedPajamaGithub


American Vision Windows is your source for the highest quality and best priced exterior windows in all of California. Specializing in vinyl double hung windows, we have the experience necessary to give you the best customer service available in Southern California. We take pride in our ability to provide service, quality, and respect to everyone of our clients and vendors.
New windows can also dramatically improve the look and feel of your home as well as the overall value of your home! New exterior windows can improve your home instantaneously and take your home to the next level instantly. And with the HERO program, you can make these improvements now and be able to finance the cost to fit your needs. HERO financing is not just another monthly bill, it's not a credit loan, and it's often transferable to the next owner of the property. If you need new windows now, but aren't sure how you can afford it, HERO is the perfect option. Find out about our HERO options today.
American Vision Windows specializes in all of your window replacement and installation needs including bay windows, garden windows, and energy efficient windows. We install many types of windows, including Anlin windows, double pane windows and even triple pane windows! Our company doesn't stop there; they also specialize in replacement doors in all types of materials such as vinyl, wood, wood-clad, fiberglass, and aluminum. Their replacement door selection includes French sliding doors, double French doors, glass sliding doors, and entry doors.
American Vision Windows is proud to be your exterior window supplier and installer in San Diego. We are proud to have one of the highest repeat rates as well as referral rates in the nation. We are dedicated to providing personalized service to each and every client in a one on one manner. Each job and each person is different and we respect that always. We will go the extra mile to ensure we can help you with all of your home improvement needs!
Milgard Window Installers San Diego from The Video Review on Vimeo.


With our table below, you can easily compare rental prices of different car rental class deals from world-trusted suppliers in Narita Airport Terminal 1.
To simplify your choice even more, we highlighted the cheapest car rental offer from each car rental providor in NRT Airport in red.
Car rental agencies in Narita Airport Terminal 1 can differ significantly not only in variety of rental vehicles and their car rental conditions, but also in average rating scores provided by our previous customers.
The array of rental cars in NRT Airport will fascinate you. Car rental classes available in Narita Airport Terminal 1 range from ECONOMY, STANDARD, FULL SIZE, LUXURY, CONVERTIBLE, VAN, SUV, EXOTIC to 7 SEATER, 9 SEATER . The biggest collection of cars in Narita Airport Terminal 1 is offered by NISSAN.
In Narita Airport Terminal 1, you can find the right supplier for any of your trips. You will find vendors that provide hourly, daily, weekly, short term, monthly, long term, and even one way car rental deals. You may also find some offers for young drivers in NRT Airport.
If you cannot find your perfect car rental deal in Narita Airport Terminal 1, remember to check offers in other pick up locations closest to NRT Airport. The diversity of cars is usually wider at the Narita Airport Terminal 1, but you may find cheaper deals in other less popular pick-up locations: small or local airports, railway stations, ports, and downtown spots.
The car rental suppliers in NRT Airport provide a huge variety of vehicles that will be suitable for any of your trips. With our table below, you will find a car that fits your purpose really easily.
Premium Business trips that start at Narita Airport Terminal 1 Buick Lacrosse, Lincoln MKS, or similar.
Luxury / Exotic Special occasions and drives of your life in Narita Airport Terminal 1 Bentley, Rolls Royce, Ferrari, or similar.
Convertible Trips on your own or with your second half to feel the freedom on Narita Airport Terminal 1 roads Ford Mustang Convertible, Chrysler 200 Convertible, or similar.
How to rent a car in Narita Airport Terminal 1 ?
How much does it cost to rent a car in NRT Airport?
The prices for renting a car in Narita Airport Terminal 1 range from $44.26 per day to $182.12 per day based on the selected model and rental options.
What is the most popular rental car in Narita Airport Terminal 1 ?
The most frequently booked vehicle by our customers in Narita Airport Terminal 1 is NISSAN SERENA. It's price starts from $120.88 in NRT Airport.
What is the most often requested car rental class in Narita Airport Terminal 1 NRT?
COMPACT is the car rental class that is most often chosen among our customers in NRT Airport. The cheapest COMPACT vehicle in Narita Airport Terminal 1 is COMPACT. Its price starts from $53.63 per day.
What is the best car rental agency in Narita Airport Terminal 1?
The car rental company that currently has the highest user rating in NRT is TIMES JAPAN with an average score of 8.4/10. The cheapest car from TIMES JAPAN in Narita Airport Terminal 1 is Toyota Corolla Axio. The current relevant price is $63.85 per day.
Which car rental companies have on-site car rental desks at Narita Airport Terminal 1?
Car rental companies with an available car rental desks at Narita Airport Terminal 1 are NISSAN , EUROPCAR .
Which car rental suppliers will pick you up at Narita Airport Terminal 1?
TIMES , BUDGET offer free shuttle bus trips from Narita Airport Terminal 1 to their car rental offices.
Can I pay for my rent-a-car reservation with a debit card in Narita Airport Terminal 1?
Should I buy an additional insurance for my car rental in Narita Airport Terminal 1 ?


Description: PRICE REDUCED!! This business is in operation and doing very good the owner is wanting to retire, he has plenty of equipment he is leaving for the next owner and the employees will stay also, he is making a list of all of the machines and equipment that is staying, please contact agent for more info. Ideal location, very good business.


text:
Q: Powershell delete row (csv file) based on compare of 2 columns I have 2 csv files neither is sorted:
File1 structure = name,loc,date,url
File2 structure = name,url
I am trying to use powershell to remove any rows from File1 that contain the same name from File2.
What I have is extremely slow based on the number of records I have and how many times this is repeated.
Is there a better way to do this?
$file1 = "file1.csv"
$file2 = "file2.csv"

$f1 = import-csv $file1
$f2 = import-csv $file2

$f1names = $f1.name
$f2names = $f2.name

$difference = compare-object -referenceobject $f1names -differenceobject $f2names -passthru

$results = $difference | % {
$f1 | ? { $f1.name -eq $_}
$f2 | ? { $f2.name -eq $_}
}

A: See if this isn't faster:
$file1 = "file1.csv"
$file2 = "file2.csv"

$f2Names = import-csv $file2 | select -ExpandProperty Name

Import-Csv $file1 |
Where { $f2Names -notcontains $_.Name }

redpajama_set_name: RedPajamaStackExchange


Japanese Communist Party Chair Shii Kazuo on February 6 attended a reception hosted by the Embassy of Vietnam in Tokyo in celebration of the Vietnamese New Year (Tet) and gave a congratulatory speech.
Shii congratulated the successful development of the Vietnamese undertakings based on its Doi Moi policy in which the economic growth of 8.5 percent, a record high in the past 10 years, was achieved in 2007.
Shii also congratulated Vietnam for its recent election to the post of non-permanent member of the U.N. Security Council for the first time and expressed his hope that Vietnam, a country that had long fought against wars of aggression, will contribute to world peace in the United Nations.
The ambassador thanked Shii for his participation in the reception and expressed his gratitude to the JCP for its consistent support for the national liberation struggle of the Vietnamese people from before the establishment of diplomatic relations between Japan and Vietnam.


text:
The Gambia (1)
East Asia and the Pacific (64)
Europe and Eurasia (28)
Near East (33)
South and Central Asia (16)
Western Hemisphere (31)
(-) Exporting (172)
Regulations & Requirements (82)
Trade Missions (17)
Virtual Trade Events (1)
Buying U.S. Products (8)
Trade Policy (35)
Multilateral Engagement (3)
CAFTA-DR (2)
China Phase One Agreement (1)
Japan Trade Agreement (1)
Panama Trade Agreement (1)
U.S.-Mexico-Canada Agreement (USMCA) (1)
Attaché Report (GAIN) (151)
International Agricultural Trade Report (15)
Quarterly Agricultural Export Forecast (1)
(-) Fruits and Vegetables (172)
Citrus Fruit (16)
Deciduous Fruit (13)
Dried Fruit (24)
Stone Fruit (14)
(-) Grain and Feed (172)
Corn (50)
Forage and Hay (2)
Grain Sorghum (13)
Biofuels (7)
Dairy (90)
Fish and Seafood (64)
Horticulture and Forestry (28)
Livestock and Meats (98)
Beef and Cattle (62)
Pork and Hogs (39)
Oilseeds (91)
Other Oilseeds (19)
Soybeans (52)
Organic Products (9)
Poultry and Eggs (74)
Processed Food Products (85)
Pulse Crops (21)
Seeds (Planting) (22)
Tree Nuts (73)
Wine, Beer and Spirits (41)
(-) Fruits and Vegetables
Quarterly Agricultural Export Forecast
FAS and the Economic Research Service jointly publish the quarterly Outlook for U.S. Agricultural Trade. The agricultural export forecast is updated February, May, August, and November.
India: Bulk Food Labeling Requirements Remain in Effect
India's 2012 and 2016 labeling requirements for bulk food products will remain in effect until new standards and regulations take effect in November.
Philippines: Plant-based Food Products Market Brief
Supported by growing awareness and adoption of global trends, plant-based foods in the Philippines have taken hold of a recognizable niche marketplace in the past year.
2020 Agricultural Export Yearbook
The 2020 U.S. Agricultural Export Yearbook provides a statistical summary of U.S. agricultural commodity exports to the world. This summary lists only the United States' primary trading partners.
Opportunities for U.S. Agricultural Exports to Southern Asia
South Asia, which includes Afghanistan, Bangladesh, Bhutan, India, Maldives, Nepal, Pakistan, and Sri Lanka, accounts for 24 percent of the world's population, with 1.84 billion people in 2019.
U.S. Sorghum Prices Rally with China's Return to the Market
China's announcement of tariff exclusions following the Phase One agreement had an immediate impact on U.S. sorghum exports mainly because of the sheer volume China purchases and its effect on prices.
Russia: EAEU Allows Temporary Duty-Free Imports of Critical Foods
On April 3, 2020, the Eurasian Economic Commission (EEC), which is the regulatory body of the Armenia-Belarus-Kazakhstan-Kyrgyzstan-Russia Eurasian Economic Union (EAEU), issued ...
China: China Announces a New Round of Tariff Exclusions
On February 18, 2020, China announced a new round of tariff exclusions for U.S. agricultural commodities impacted by the retaliatory Section 301-tariffs levied by China.
Canada: Food Service - Hotel Restaurant Institutional
The Canadian foodservice sector grew by 5 percent to nearly $68 billion USD in 2018.
Kenya: Agricultural Biotechnology Annual
The first planting of Bt. Cotton for commercialization in Kenya is planned for the start of the long rains season, which is expected in March or April 2020.
China: China Announces Reductions in Certain Additional Tariffs
On February 6, 2020, the State Council Tariff Commission announced that China would cut in half the additional tariffs for certain commodities from the United States on February 14, 2020.
Taiwan: Taiwan Impacted as Agricultural Trade Disrupted by Coronavirus
Agricultural trade with Taiwan is being impacted by new quarantine measures, port closures, vessel delays, and suspended flights as U.S. agribusiness exporters struggle to find available space....
Next page next »


text:
Q: Image Height: 100% in a grid. Safari vs Firefox. Why does Safari default images to the viewheight? hyhy, I've started a website and designed it for Firefox, but did the rounds and found Safari was not treating images in the same way. I'm running into issues with Safari making every single image's height the same as the browser's viewheight/viewport height. Here is a dummy site that you can pull up a version in Firefox and Safari to compare.
I'm running a 15 column grid without an exact column width (1 fr is used) or row height (min-content is used). In Firefox, the browser finds the width of the image based on how many columns the image spans out of the 15 total columns, and then proportionately sets the image's height. In Safari, the image has the same height as the browser window itself with no regard for how much of the width of the photo is cutoff and the extra burden of scrolling caused by this behavior.
Solutions attempted so far:

*

*Defining a max row height: This solution gives Safari a pixel dimension to scale my image to and things behave correctly on images. However, the accordion at the bottom of the page has too much content when open, and not much content when closed, which requires a dynamic min-content sizing approach. When applying a maximum row height to my grid (example: grid-auto-rows: 175px;), the accordion's content extends past the grid. The result is the content of my accordion is not respected, the grid ends too soon and my footer inserts itself while the rest of my content balloons out the bottom of the page.

*Define each image's px height in css: This solution just changes how the website looks in Firefox, with images not filling up the vertical spaces they need to.

*Define each image's height as less than 100% of its parent element: This does indeed make the images smaller in Safari, since the browsers reads height: 100% and defaults to the browser's viewport height. However, this clearly breaks in Firefox and images fill less than 100% of the defined Row span.

This grid system is used all over my website, which is very photograph-forward. I'd love to not define a max row height, but Safari seems to be desperate for a defined row height but not want to do it the way Firefox does.
My hope? Some sort of minmax(min-content, defined-value) replacement that let's me have rows with zero content have zero height, rows with lots of content have matching height to that content and still have a 'default'? value for images to get their sizes from? What can I do here to get the look of the Firefox version to show up on Safari?


On one of the first few days while being sick, miserable, and lonely - this elderly lady using a metal pipe as a walking stick left quite a mark on my heart. We chatted for a while - she said she did not have children - and really, really wanted to go to 'my wedding in SG'. While I wasn't sure if I would ever see her again...we exchanged phone numbers and I hope she's doing well.


text:
'use strict';

const cp = require('child_process');
const {assert} = require('chai');
const {describe, it} = require('mocha');

const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'});

const cmd = 'node analyze-streaming-automl-object-tracking.js';
const modelId = 'VOT409893536788381696';
const project = process.env.GCLOUD_PROJECT;
const file = 'resources/googlework_short.mp4';

describe.skip('streaming automl object tracking', function () {
  this.retries(3);
  it('should track an object in a streaming video', async () => {
    const output = execSync(`${cmd} ${file} ${project} ${modelId}`);
    assert.match(output, /Track id/);
  });
});


Hidden among the lush undulating hills of Bali's culture-rich highlands, this most private of resorts sits among thriving rice fields as far as the eye can see. Just outside the artists' village of Ubud, the former estate of Hendra Hadiprana, one of Indonesia's most respected art collectors has been transformed into an intimate retreat with twenty individual villas, each one uniquely featuring original Balinese artworks from his collection.
Surrounded with the lush greenery and breathtaking view of the rice fields, Kenanga Hotel offers you the authentic getaway in Ubud with a comfort and great hospitality .
Nested in Ubud, Bali's Centre of Arts. The culture is so rich that almost no week is past without a ceremony held somewhere in Ubud. This is the highland of Bali, and with uninterrupted views of terraced rice fields is this beautiful resort.All rooms are comfortable and cosy with all modern amenities including free Wi-Fi. Exotic during the day and romantic during the night, a 2 meter wide subek runs right through the middle of the resort, serenading the resort by sound of soft flowing water.This is the place to be in, back to nature. Away from the hustle and bustle of the city.
carefully created to fulfill upscale travelers expectation for a comfortable stay in a spacious hotel room, surrounded by natural Ubud's beauty, where the staff are always respond promptly and discreetly to meet everything the guests need.
located in Ubud, situated in a private area away from the sounds of the lively city, completely submerged in Bali's peaceful tropical environment. The Samara is not directly accessible by car. That's why you will enjoy pure tranquillity and no pollution. The closest pick-up/drop off point (our parking area) is 400 meters away. And don't worry, the staff will take care of all your belongings.The area is surrounded by rice fields and palm trees, and caressed by a light breeze that will awaken your senses. The view of the Agung volcano completes the incredible panorama that will accompany you throughout your stay.
Located in Canggu area, it is about 35 minutes from Kuta. Canggu is becoming better known in tourist and expat circles and living in Seminyak, you can feel the way things are moving. Canggu is a place that most surfers will know about, apart from that, it is quiet, very local and offers lovely views of rice fields as opposed to cement in Seminyak.
Fusing modern, stylish elegance with subtle Balinese touches, the twelve fully appointed luxury villas introduce a new level of sumptuous holiday luxury.Each villa features an intimate tropical garden containing a private pool and outdoor drencher shower.Sun loungers and relaxation pavilions complete the outdoor picture and ensure plenty of places for sun-worshippers to lounge the day away. The bright and breezy interiors feature simple white walls and rich wooden floors and furnishings to give a clean, natural feel.A large open plan Smeg gourmet kitchen and dining area is separated from the main living space, creating a practical area for entertaining guests or catering for the whole family.
the southwest coast of Bali might just be the very definition of paradise. Located near the island's famed Tanah Lot Temple, this luxurious beachfront resort leads you to depths of relaxation you never knew existed. Nestled between lush rice fields and glistening volcanic black sands lapped by the Indian Ocean, these luxury villas in Bali fully integrate the beach experience in its vibrant mix of spaces. The perfectly calibrated balance of tranquillity and vitality, that is so Bali, does a fine job of soothing and stimulating the senses.
The villas concept is accessible for everyone. Room design and public area are fully wheelchair accessible, with handrails, minibus access with lift and sloping routes constracted. Property features with large bedroom, ensuite bathroom with huge bathtub, inside and outside shower and wheel in shower. The two outdoor swimming pool have access and equiptable as well for individuals with disabilities.
Being situated in this traditional area, Bali Mountain Retreat's guests are given a unique experience with the local Balinese people and their culture.You will welcome the refreshing and revitalizing mountain air, clean spring fed water and panoramic views to the coast of Bali. Enjoy walking through the terraced rice fields or into the forest to a sacred Hindu temple. Stroll to the local village past the cacao and coffee plantations or simply relax, unwind and take in the natural beauty of the landscape.
luxury nature resort, spa and boutique hotel in North Bali on a working coffee plantation located close by Bali's central lakes and Munduk hill station. Our hillsides are covered with jungle, coffee trees, rice fields, and pretty much anything that grows on the island. With just a handful of villas and suites on some 5 hectares of land you can enjoy all this in privacy and genuinely relax far away from the crowds.
Hidden in the jungle-clad hills overlooking Bali's North coast, you find a retreat dedicated to the finer things in life: beauty, tranquility and very good food. Here, just a few hours from the airport you feel light years away from the crowds, ready to experience the true Bali in all its splendor. Visit some of the island's most iconic attractions or find your own hidden gems. Or maybe just lounge by your pool, enjoying views of volcanoes in distant Java as the tropical breeze brings you fragrant hints from nearby spice plantations .
Nestled on the beach, between the Bedugul mountain range and the Bali sea, this tropical island gem is surrounded by various attractions. Dolphin watching, trekking to waterfalls, hot springs and diving or snorkelling around Menjangan island, home to the best preserved coral reef in Bali.Just two and half hours drive from the hustle and bustle of the busy tourist areas in the south, you will enjoy a relaxing Bali holiday.
36 spacious rooms and two 2-Villa suites with private pool provide quality, comfort and luxury ideal for families, honeymooners or leisure travelers. Enjoy the free form infinity pool and open-air restaurant with spectacular views of the sea and the tropical gardens, while the Jaya Spa is an oasis for those who seek relaxation and rejuvenation .


The CRiB (Curry Rivel in Bloom) group have obtained helpful sponsorship from Travis Perkins in Langport, who donated over £100 worth of Postcrete to enable 7 posts to be firmly fixed into position round the war memorial garden.
The posts will have hanging baskets on them, and the posts will be linked by rope swags. The planting will be in the 2017 theme colour of purple and gold.
The photo shows Chair of CRiB, Peter Goodenough, with Ian Ilot, manager of Travis Perkins in Langport, collecting the donated Postcrete.
There will be a large willow fence which will be made on site around the boundary of the garden, which is being produced by Coates of Stoke St. Gregory and they are sponsoring part of the cost with the rest being met by the Parish Council.
What is needed is local sponsorship of the posts and some mechanical help with digging out the bottom of the post holes — where volunteers have met solid rock. if anyone can help, please do make contact.
Many thanks go to Mr James Duder of County Stores, owners of the petrol station, who will be making a financial donation towards the War Memorial new planting scheme. There will be an official presentation to CRiB at the end of February when the petrol station will be relaunched as a BP garage.
Look out for the purple crocuses planted by the Twinning Tree next to the petrol station, they should be flowering by now; planted as part of the Purple4Polio campaign, orchestrated by The Rotary Club of Langport and Somerton, over 6000 purple crocus corms have been planted in the area, with over 500 being planted in Curry Rivel. Across the UK over 6 million crocus corms are being planted.
The public meeting on 22nd February at 7.30pm in the Village Hall will be a chance to discuss with members of the community how they can get involved, so please do come along.


Jay Wadwala Engineering Works can be found at 1, Anand Estate, Nr Ctm, Amraiwadi, . The following is offered: Machinery & Parts Mfrs - Pharmaceuticals . In Ahmedabad there are 169 other Machinery & Parts Mfrs - Pharmaceuticals. An overview can be found here.


Question: When and where did the first recognizably modern humans appear?
Donald Johanson: Yeah, that's a… The appearance of modern, or of homo sapiens, of someone you wouldn't feel too uncomfortable sitting next to at, say, Lincoln Center, surprising answer probably. Going back to probably about 200,000 years now there were fossils found in the late 1960s in Southern Ethiopia in a place called the Kibish or in the Omo region, and those fossils have been preserved in the Ethiopian National Museum ever since their discovery and we thought they were maybe at the most 90,000 years old. A research team has gone back and dated a geological horizon at the site and they are close to 200,000 years old and they have skulls like yours and mine, so they would have appeared with muscles and flesh and so on very much like we do, so these would justifiably be put into our own species, homo sapiens, supposedly wise man and… I know I question that too every time I watch the evening news, but it means that the earliest members of our species appeared in Africa. There are also fossils from Southern Africa that suggest 100 to 150,000 years and we're beginning to find evidence in South Africa of things like the use of ochre, the manufacture of bone and bone tools, the manufacture of blade tools and various technologies that don't show up in Europe until 40,000 years old. Yet, in South Africa there as much as 160,000 years, so homo sapiens can certainly be traced back to at least 160,000 and I would say to somewhere around 200,000 years ago.
Question: What happened to the Neanderthals, and was it our fault?
Donald Johanson: Well I don't think we interbred with the Neanderthals at all. There are some people who think that there was some level of interbreeding. I think that we look so biologically different that we looked and we acted so different and we culturally were so different that we would not have had interbreeding between two species. I call them Neanderthal homos and Neanderthalensis and modern humans, homo sapiens. Neanderthals evolved in Europe as an isolated group. That is one of the conditions for the development of a new species, that they're isolated genetically by a geographical barrier or whatever from other populations, and they evolved and adapted to glacial Europe. They lived there for a few hundred thousand years. We left Africa 40 to 50,000 years ago with a very sophisticated technology, with an incredibly creative mind, with division of labor, with a whole series of things that were very different from Neanderthals, and when we began to compete with Neanderthals for game and for territory Neanderthals fled and the latest surviving Neanderthals we have are found in Gibraltar at about 28,000 years ago. So I think that as we moved into Europe from the Middle East Neanderthals moved westward, ultimately down into the Iberian Peninsula where they hung on until about 28,000 years ago and ultimately went extinct.
Don Johanson believes we didn't interbreed with our ancient rivals—we just outcompeted them.